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 CraftingCategories v1.0.1
plugins/CraftingCategories.dll
Decompiled a week agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using GUIFramework; using HarmonyLib; using Jotunn.Entities; using Jotunn.Utils; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("assembly_utils")] [assembly: IgnoresAccessChecksTo("assembly_valheim")] [assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")] [assembly: AssemblyCompany("CraftingCategories")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("CraftingCategories")] [assembly: AssemblyTitle("CraftingCategories")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace CraftingCategories { internal class CategoryPanel : MonoBehaviour { public static CategoryPanel Instance; private const float Padding = 8f; private const float Gap = 6f; private const float SearchHeight = 34f; private static readonly Color SelectedTint = new Color(1f, 0.72f, 0.32f, 1f); private static readonly Color SelectedText = new Color(1f, 0.84f, 0.5f, 1f); private static readonly Color CountText = new Color(0.75f, 0.75f, 0.75f, 1f); private InventoryGui _gui; private RectTransform _root; private RectTransform _content; private ScrollRect _scroll; private TMP_InputField _search; private Button _craftableButton; private Image _craftableImage; private TMP_Text _craftableLabel; private Button _templateButton; private Image _templateImage; private TMP_Text _templateText; private ButtonSfx _templateSfx; private readonly List<GameObject> _buttons = new List<GameObject>(); private string _signature; private bool _layoutDirty = true; private int _shownFrame; private Vector2Int _screenSize; private bool _refreshRequested; private bool _loggedLayout; public bool SearchFocused { get { if (Object.op_Implicit((Object)(object)_search)) { return _search.isFocused; } return false; } } public void RequestRefresh() { _refreshRequested = true; } public static void Ensure(InventoryGui gui) { //IL_004d: 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) if ((!Object.op_Implicit((Object)(object)Instance) || !((Object)(object)Instance._gui == (Object)(object)gui)) && Object.op_Implicit((Object)(object)gui) && Object.op_Implicit((Object)(object)gui.m_crafting)) { GameObject val = new GameObject("CraftingCategoriesPanel", new Type[1] { typeof(RectTransform) }); val.transform.SetParent((Transform)(object)gui.m_crafting, false); Instance = val.AddComponent<CategoryPanel>(); Instance.Build(gui); } } private void OnDestroy() { if ((Object)(object)Instance == (Object)(object)this) { Instance = null; } } private void OnEnable() { _shownFrame = Time.frameCount; } private void Update() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0026: 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) _ = InputGuard.Blocking; Vector2Int val = default(Vector2Int); ((Vector2Int)(ref val))..ctor(Screen.width, Screen.height); if (val != _screenSize) { _screenSize = val; _layoutDirty = true; } if (_layoutDirty && Time.frameCount - _shownFrame > 5 && InventoryGui.IsVisible()) { ApplyLayout(); } if (_refreshRequested) { _refreshRequested = false; CraftFilter.Refresh(scrollToTop: false); } if (Object.op_Implicit((Object)(object)_search) && ((Component)_search).gameObject.activeInHierarchy && !SearchFocused && ZInput.GetKey((KeyCode)306, true) && ZInput.GetKeyDown((KeyCode)102, true)) { ((Selectable)_search).Select(); _search.ActivateInputField(); } } public void OnInventoryHidden() { if (Object.op_Implicit((Object)(object)_search) && _search.isFocused) { _search.DeactivateInputField(false); } } public void ClearSearchField() { if (Object.op_Implicit((Object)(object)_search)) { _search.SetTextWithoutNotify(""); } } public void OnConfigChanged() { ((Component)this).gameObject.SetActive(Plugin.Enabled.Value); if (Plugin.SearchEnabled != Object.op_Implicit((Object)(object)_search)) { if (Object.op_Implicit((Object)(object)_search)) { Object.Destroy((Object)(object)((Component)_search).gameObject); _search = null; CraftFilter.SearchText = ""; } else { CreateSearchField(); } } _layoutDirty = true; _signature = null; ApplyLayout(); CraftFilter.Refresh(scrollToTop: false); } private void Build(InventoryGui gui) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_0170: Expected O, but got Unknown //IL_019c: Unknown result type (might be due to invalid IL or missing references) //IL_01d8: Unknown result type (might be due to invalid IL or missing references) //IL_01de: Expected O, but got Unknown //IL_01f7: Unknown result type (might be due to invalid IL or missing references) //IL_0201: Expected O, but got Unknown //IL_0211: Unknown result type (might be due to invalid IL or missing references) //IL_022b: Unknown result type (might be due to invalid IL or missing references) //IL_0245: Unknown result type (might be due to invalid IL or missing references) //IL_0255: Unknown result type (might be due to invalid IL or missing references) //IL_0265: Unknown result type (might be due to invalid IL or missing references) //IL_02d0: Unknown result type (might be due to invalid IL or missing references) //IL_02da: Expected O, but got Unknown //IL_0380: Unknown result type (might be due to invalid IL or missing references) //IL_038a: Expected O, but got Unknown //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) _gui = gui; _root = (RectTransform)((Component)this).transform; _templateButton = gui.m_tabCraft; _templateImage = (Object.op_Implicit((Object)(object)_templateButton) ? ((Component)_templateButton).GetComponent<Image>() : null); _templateText = (Object.op_Implicit((Object)(object)_templateButton) ? ((Component)_templateButton).GetComponentInChildren<TMP_Text>(true) : null); if (!Object.op_Implicit((Object)(object)_templateText)) { _templateText = gui.m_recipeName; } _templateSfx = (Object.op_Implicit((Object)(object)_templateButton) ? ((Component)_templateButton).GetComponent<ButtonSfx>() : null); Image val = ((Component)this).gameObject.AddComponent<Image>(); Image component = ((Component)gui.m_crafting).GetComponent<Image>(); if (Object.op_Implicit((Object)(object)component) && Object.op_Implicit((Object)(object)component.sprite)) { val.sprite = component.sprite; val.type = component.type; ((Graphic)val).color = ((Graphic)component).color; val.pixelsPerUnitMultiplier = component.pixelsPerUnitMultiplier; ((Graphic)val).material = ((Graphic)component).material; } else { ((Graphic)val).color = new Color(0f, 0f, 0f, 0.65f); } GameObject val2 = new GameObject("Scroll", new Type[4] { typeof(RectTransform), typeof(Image), typeof(RectMask2D), typeof(ScrollRect) }); val2.transform.SetParent((Transform)(object)_root, false); ((Graphic)val2.GetComponent<Image>()).color = new Color(0f, 0f, 0f, 0.001f); GameObject val3 = new GameObject("Content", new Type[3] { typeof(RectTransform), typeof(VerticalLayoutGroup), typeof(ContentSizeFitter) }); val3.transform.SetParent(val2.transform, false); _content = (RectTransform)val3.transform; _content.anchorMin = new Vector2(0f, 1f); _content.anchorMax = new Vector2(1f, 1f); _content.pivot = new Vector2(0.5f, 1f); _content.offsetMin = Vector2.zero; _content.offsetMax = Vector2.zero; VerticalLayoutGroup component2 = val3.GetComponent<VerticalLayoutGroup>(); ((HorizontalOrVerticalLayoutGroup)component2).spacing = 3f; ((HorizontalOrVerticalLayoutGroup)component2).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)component2).childControlHeight = true; ((HorizontalOrVerticalLayoutGroup)component2).childForceExpandWidth = true; ((HorizontalOrVerticalLayoutGroup)component2).childForceExpandHeight = false; val3.GetComponent<ContentSizeFitter>().verticalFit = (FitMode)2; _scroll = val2.GetComponent<ScrollRect>(); _scroll.content = _content; _scroll.viewport = (RectTransform)val2.transform; _scroll.horizontal = false; _scroll.vertical = true; _scroll.movementType = (MovementType)2; _scroll.scrollSensitivity = 40f; GameObject val4 = CreateButton((Transform)(object)_root, "CraftableOnly", out _craftableButton, out _craftableImage); _craftableLabel = CreateText(val4.transform, "Label", Loc.CraftableOnly, (TextAlignmentOptions)514, 1f); Stretch(_craftableLabel.rectTransform, 8f, 8f); ((UnityEvent)_craftableButton.onClick).AddListener((UnityAction)delegate { CraftFilter.SetCraftableOnly(!CraftFilter.CraftableOnly); UpdateCraftableVisual(); }); UpdateCraftableVisual(); if (Plugin.SearchEnabled) { CreateSearchField(); } if (Object.op_Implicit((Object)(object)((Component)gui.m_crafting).GetComponentInParent<Mask>()) || Object.op_Implicit((Object)(object)((Component)gui.m_crafting).GetComponent<RectMask2D>())) { Plugin.Log.LogWarning((object)"Crafting panel is masked - the sidebar may be clipped."); } ApplyLayout(); ((Component)this).gameObject.SetActive(Plugin.Enabled.Value); } private void CreateSearchField() { _search = CloneVanillaSearchField() ?? CreatePlainSearchField(); ((Object)((Component)_search).gameObject).name = "Search"; _search.restoreOriginalTextOnEscape = false; _search.lineType = (LineType)0; _search.SetTextWithoutNotify(CraftFilter.SearchText); Graphic placeholder = _search.placeholder; TMP_Text val = (TMP_Text)(object)((placeholder is TMP_Text) ? placeholder : null); if (val != null) { val.text = Loc.Search + " (Ctrl+F)"; } ((UnityEvent<string>)(object)_search.onValueChanged).AddListener((UnityAction<string>)CraftFilter.SetSearch); ((Component)_search).gameObject.SetActive(true); _layoutDirty = true; } private TMP_InputField CloneVanillaSearchField() { try { BuildUi val = (Object.op_Implicit((Object)(object)Hud.instance) ? Hud.instance.m_buildUi : null); GuiInputField val2 = (Object.op_Implicit((Object)(object)val) ? val.m_searchField : null); if (!Object.op_Implicit((Object)(object)val2)) { return null; } GameObject obj = Object.Instantiate<GameObject>(((Component)val2).gameObject, (Transform)(object)_root, false); GuiInputField component = obj.GetComponent<GuiInputField>(); DisablePersistent((UnityEventBase)(object)((TMP_InputField)component).onValueChanged); DisablePersistent((UnityEventBase)(object)((TMP_InputField)component).onEndEdit); DisablePersistent((UnityEventBase)(object)((TMP_InputField)component).onSubmit); DisablePersistent((UnityEventBase)(object)((TMP_InputField)component).onSelect); DisablePersistent((UnityEventBase)(object)((TMP_InputField)component).onDeselect); DisablePersistent((UnityEventBase)(object)component.OnInputSubmit); UIGamePad[] componentsInChildren = obj.GetComponentsInChildren<UIGamePad>(true); foreach (UIGamePad val3 in componentsInChildren) { if (Object.op_Implicit((Object)(object)val3.m_hint)) { Object.Destroy((Object)(object)val3.m_hint); } Object.Destroy((Object)(object)val3); } ((TMP_InputField)component).text = ""; return (TMP_InputField)(object)component; } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not clone the build menu search field, using a plain one: " + ex.Message)); return null; } } private static void DisablePersistent(UnityEventBase evt) { if (evt != null) { for (int i = 0; i < evt.GetPersistentEventCount(); i++) { evt.SetPersistentListenerState(i, (UnityEventCallState)0); } } } private TMP_InputField CreatePlainSearchField() { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown //IL_005e: 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_0093: Expected O, but got Unknown //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Expected O, but got Unknown //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Expected O, but got Unknown //IL_018f: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("Search", new Type[2] { typeof(RectTransform), typeof(Image) }); val.SetActive(false); val.transform.SetParent((Transform)(object)_root, false); ((Graphic)val.GetComponent<Image>()).color = new Color(0f, 0f, 0f, 0.55f); GameObject val2 = new GameObject("Text Area", new Type[2] { typeof(RectTransform), typeof(RectMask2D) }); val2.transform.SetParent(val.transform, false); Stretch((RectTransform)val2.transform, 10f, 10f); TMP_Text val3 = CreateText(val2.transform, "Text", "", (TextAlignmentOptions)4097, 1f); Stretch(val3.rectTransform, 0f, 0f); TMP_Text val4 = CreateText(val2.transform, "Placeholder", Loc.Search, (TextAlignmentOptions)4097, 1f); Stretch(val4.rectTransform, 0f, 0f); val4.fontStyle = (FontStyles)2; ((Graphic)val4).color = new Color(1f, 1f, 1f, 0.45f); TMP_InputField obj = val.AddComponent<TMP_InputField>(); obj.textViewport = (RectTransform)val2.transform; obj.textComponent = val3; obj.placeholder = (Graphic)(object)val4; obj.fontAsset = val3.font; obj.pointSize = val3.fontSize; obj.caretColor = Color.white; obj.customCaretColor = true; return obj; } private GameObject CreateButton(Transform parent, string name, out Button button, out Image image) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name, new Type[4] { typeof(RectTransform), typeof(Image), typeof(Button), typeof(LayoutElement) }); val.transform.SetParent(parent, false); image = val.GetComponent<Image>(); if (Object.op_Implicit((Object)(object)_templateImage)) { image.sprite = _templateImage.sprite; image.type = _templateImage.type; ((Graphic)image).color = ((Graphic)_templateImage).color; image.pixelsPerUnitMultiplier = _templateImage.pixelsPerUnitMultiplier; ((Graphic)image).material = ((Graphic)_templateImage).material; } else { ((Graphic)image).color = new Color(0.25f, 0.2f, 0.15f, 0.9f); } button = val.GetComponent<Button>(); ((Selectable)button).targetGraphic = (Graphic)(object)image; if (Object.op_Implicit((Object)(object)_templateButton)) { ((Selectable)button).transition = ((Selectable)_templateButton).transition; ((Selectable)button).colors = ((Selectable)_templateButton).colors; ((Selectable)button).spriteState = ((Selectable)_templateButton).spriteState; } Button obj = button; Navigation navigation = default(Navigation); ((Navigation)(ref navigation)).mode = (Mode)0; ((Selectable)obj).navigation = navigation; if (Object.op_Implicit((Object)(object)_templateSfx)) { ButtonSfx obj2 = val.AddComponent<ButtonSfx>(); obj2.m_sfxPrefab = _templateSfx.m_sfxPrefab; obj2.m_sfxPrefabVibrationOnly = _templateSfx.m_sfxPrefabVibrationOnly; obj2.m_selectSfxPrefab = _templateSfx.m_selectSfxPrefab; obj2.m_selectSfxPrefabVibrationOnly = _templateSfx.m_selectSfxPrefabVibrationOnly; obj2.m_enterSfxPrefab = _templateSfx.m_enterSfxPrefab; obj2.m_enterSfxPrefabVibrationOnly = _templateSfx.m_enterSfxPrefabVibrationOnly; } LayoutElement component = val.GetComponent<LayoutElement>(); component.preferredHeight = Plugin.ButtonHeight.Value; component.minHeight = Plugin.ButtonHeight.Value; component.flexibleWidth = 1f; return val; } private TMP_Text CreateText(Transform parent, string name, string value, TextAlignmentOptions align, float sizeFactor) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name, new Type[2] { typeof(RectTransform), typeof(TextMeshProUGUI) }); val.transform.SetParent(parent, false); TextMeshProUGUI component = val.GetComponent<TextMeshProUGUI>(); if (Object.op_Implicit((Object)(object)_templateText)) { ((TMP_Text)component).font = _templateText.font; ((TMP_Text)component).fontSharedMaterial = _templateText.fontSharedMaterial; } ((TMP_Text)component).fontSize = Mathf.Clamp(Plugin.ButtonHeight.Value * 0.52f, 12f, 26f) * sizeFactor; ((TMP_Text)component).textWrappingMode = (TextWrappingModes)0; ((TMP_Text)component).overflowMode = (TextOverflowModes)1; ((TMP_Text)component).alignment = align; ((Graphic)component).color = Color.white; ((Graphic)component).raycastTarget = false; ((TMP_Text)component).text = value; return (TMP_Text)(object)component; } private static void Stretch(RectTransform rt, float left, float right) { //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) rt.anchorMin = Vector2.zero; rt.anchorMax = Vector2.one; rt.offsetMin = new Vector2(left, 0f); rt.offsetMax = new Vector2(0f - right, 0f); } private void ApplyLayout() { //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: 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_01c2: Unknown result type (might be due to invalid IL or missing references) //IL_01c7: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_01f1: Unknown result type (might be due to invalid IL or missing references) //IL_01fc: Unknown result type (might be due to invalid IL or missing references) //IL_0206: Unknown result type (might be due to invalid IL or missing references) //IL_0211: Unknown result type (might be due to invalid IL or missing references) //IL_0222: Unknown result type (might be due to invalid IL or missing references) //IL_0237: Unknown result type (might be due to invalid IL or missing references) //IL_023c: Unknown result type (might be due to invalid IL or missing references) //IL_023d: Unknown result type (might be due to invalid IL or missing references) //IL_0247: Unknown result type (might be due to invalid IL or missing references) //IL_0248: Unknown result type (might be due to invalid IL or missing references) //IL_0252: Unknown result type (might be due to invalid IL or missing references) //IL_0266: Unknown result type (might be due to invalid IL or missing references) //IL_0278: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_018d: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)_gui) && Object.op_Implicit((Object)(object)_root)) { float value = Plugin.PanelWidth.Value; MeasureProtrusions(out var protrudeLeft, out var protrudeRight); bool flag = ResolveSide(value, protrudeLeft, protrudeRight) == PanelSide.Left; ((Transform)_root).SetAsFirstSibling(); _root.anchorMin = new Vector2(flag ? 0f : 1f, 0f); _root.anchorMax = new Vector2(flag ? 0f : 1f, 1f); _root.pivot = new Vector2(flag ? 1f : 0f, 0.5f); _root.sizeDelta = new Vector2(value, 0f); float num = (flag ? (0f - (6f + protrudeLeft)) : (6f + protrudeRight)); _root.anchoredPosition = new Vector2(num + Plugin.OffsetX.Value, Plugin.OffsetY.Value); float num2 = 8f; if (Object.op_Implicit((Object)(object)_search)) { RectTransform val = (RectTransform)((Component)_search).transform; val.anchorMin = new Vector2(0f, 1f); val.anchorMax = new Vector2(1f, 1f); val.pivot = new Vector2(0.5f, 1f); val.anchoredPosition = new Vector2(0f, -8f); val.sizeDelta = new Vector2(-16f, 34f); ((Transform)val).localScale = Vector3.one; num2 += 40f; } float value2 = Plugin.ButtonHeight.Value; RectTransform val2 = (RectTransform)((Component)_craftableButton).transform; val2.anchorMin = new Vector2(0f, 0f); val2.anchorMax = new Vector2(1f, 0f); val2.pivot = new Vector2(0.5f, 0f); val2.anchoredPosition = new Vector2(0f, 8f); val2.sizeDelta = new Vector2(-16f, value2); RectTransform val3 = (RectTransform)((Component)_scroll).transform; val3.anchorMin = Vector2.zero; val3.anchorMax = Vector2.one; val3.offsetMin = new Vector2(8f, 8f + value2 + 6f); val3.offsetMax = new Vector2(-8f, 0f - num2); _layoutDirty = !InventoryGui.IsVisible(); } } private void MeasureProtrusions(out float protrudeLeft, out float protrudeRight) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Expected O, but got Unknown //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Expected O, but got Unknown //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) protrudeLeft = 0f; protrudeRight = 0f; RectTransform crafting = _gui.m_crafting; Rect rect = crafting.rect; List<RectTransform> list = new List<RectTransform>(); if (Object.op_Implicit((Object)(object)_gui.m_repairButton)) { list.Add((RectTransform)((Component)_gui.m_repairButton).transform); } Selectable[] componentsInChildren = ((Component)crafting).GetComponentsInChildren<Selectable>(true); foreach (Selectable val in componentsInChildren) { if (!((Component)val).transform.IsChildOf((Transform)(object)_root)) { list.Add((RectTransform)((Component)val).transform); } } Vector3[] array = (Vector3[])(object)new Vector3[4]; foreach (RectTransform item in list) { item.GetWorldCorners(array); float num = float.MaxValue; float num2 = float.MinValue; Vector3[] array2 = array; foreach (Vector3 val2 in array2) { float x = ((Transform)crafting).InverseTransformPoint(val2).x; num = Mathf.Min(num, x); num2 = Mathf.Max(num2, x); } if (num < ((Rect)(ref rect)).xMin && ((Rect)(ref rect)).xMin - num < 300f) { protrudeLeft = Mathf.Max(protrudeLeft, ((Rect)(ref rect)).xMin - num); } if (num2 > ((Rect)(ref rect)).xMax && num2 - ((Rect)(ref rect)).xMax < 300f) { protrudeRight = Mathf.Max(protrudeRight, num2 - ((Rect)(ref rect)).xMax); } } if (!_loggedLayout) { _loggedLayout = true; Plugin.Log.LogInfo((object)$"Crafting panel {rect}; widgets stick out left {protrudeLeft:0}, right {protrudeRight:0}"); } } private PanelSide ResolveSide(float width, float protrudeLeft, float protrudeRight) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Expected O, but got Unknown //IL_0066: 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_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008e: 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_00a0: Unknown result type (might be due to invalid IL or missing references) if (Plugin.Side.Value != PanelSide.Auto) { return Plugin.Side.Value; } Canvas componentInParent = ((Component)_gui).GetComponentInParent<Canvas>(); componentInParent = (Object.op_Implicit((Object)(object)componentInParent) ? componentInParent.rootCanvas : null); if (!Object.op_Implicit((Object)(object)componentInParent)) { return PanelSide.Left; } RectTransform val = (RectTransform)((Component)componentInParent).transform; Vector3[] array = (Vector3[])(object)new Vector3[4]; _gui.m_crafting.GetWorldCorners(array); float x = ((Transform)val).InverseTransformPoint(array[0]).x; float x2 = ((Transform)val).InverseTransformPoint(array[2]).x; Rect rect = val.rect; float num = x - ((Rect)(ref rect)).xMin; rect = val.rect; float num2 = ((Rect)(ref rect)).xMax - x2; if (num >= width + 6f + protrudeLeft) { return PanelSide.Left; } if (num2 >= width + 6f + protrudeRight) { return PanelSide.Right; } if (!(num - protrudeLeft >= num2 - protrudeRight)) { return PanelSide.Right; } return PanelSide.Left; } public void SetCategories(List<CategoryEntry> categories, string selectedKey) { //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_014e: 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_015a: Unknown result type (might be due to invalid IL or missing references) //IL_015f: 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_0167: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_01a6: Unknown result type (might be due to invalid IL or missing references) //IL_036f: Unknown result type (might be due to invalid IL or missing references) //IL_0379: Expected O, but got Unknown //IL_0334: Unknown result type (might be due to invalid IL or missing references) //IL_0340: Unknown result type (might be due to invalid IL or missing references) //IL_02bf: Unknown result type (might be due to invalid IL or missing references) //IL_02d4: Unknown result type (might be due to invalid IL or missing references) //IL_02e9: Unknown result type (might be due to invalid IL or missing references) //IL_02fb: Unknown result type (might be due to invalid IL or missing references) //IL_030f: Unknown result type (might be due to invalid IL or missing references) //IL_0324: Unknown result type (might be due to invalid IL or missing references) //IL_031d: Unknown result type (might be due to invalid IL or missing references) string text = Signature(categories, selectedKey); if (text == _signature) { return; } _signature = text; foreach (GameObject button2 in _buttons) { Object.Destroy((Object)(object)button2); } _buttons.Clear(); float value = Plugin.ButtonHeight.Value; foreach (CategoryEntry category in categories) { bool flag = category.Key == selectedKey; Button button; Image image; GameObject val = CreateButton((Transform)(object)_content, "Category_" + category.Key, out button, out image); Sprite val2 = ((!Plugin.ShowModIcons.Value) ? null : category.Source?.Icon); float left = 10f; if (Object.op_Implicit((Object)(object)val2)) { GameObject val3 = new GameObject("Icon", new Type[2] { typeof(RectTransform), typeof(Image) }); val3.transform.SetParent(val.transform, false); Image component = val3.GetComponent<Image>(); component.sprite = val2; component.preserveAspect = true; ((Graphic)component).raycastTarget = false; RectTransform val4 = (RectTransform)val3.transform; Vector2 anchorMin = (val4.anchorMax = new Vector2(0f, 0.5f)); val4.anchorMin = anchorMin; val4.pivot = new Vector2(0f, 0.5f); val4.sizeDelta = new Vector2(value - 8f, value - 8f); val4.anchoredPosition = new Vector2(5f, 0f); left = value + 2f; } bool flag2 = NewRecipes.Enabled && category.NewCount > 0; float num = (Plugin.ShowCounts.Value ? 38f : 0f) + (flag2 ? 34f : 0f); TMP_Text val6 = CreateText(val.transform, "Label", category.Name, (TextAlignmentOptions)4097, 1f); Stretch(val6.rectTransform, left, num + 6f); if (Plugin.ShowCounts.Value || flag2) { string text2 = (Plugin.ShowCounts.Value ? category.Count.ToString() : ""); if (flag2) { text2 = $"<color=#8CFF5A><b>+{category.NewCount}</b></color> " + text2; } TMP_Text obj = CreateText(val.transform, "Count", text2.TrimEnd(), (TextAlignmentOptions)4100, 0.85f); RectTransform rectTransform = obj.rectTransform; rectTransform.anchorMin = new Vector2(1f, 0f); rectTransform.anchorMax = new Vector2(1f, 1f); rectTransform.pivot = new Vector2(1f, 0.5f); rectTransform.sizeDelta = new Vector2(num, 0f); rectTransform.anchoredPosition = new Vector2(-8f, 0f); ((Graphic)obj).color = (flag ? SelectedText : CountText); } if (flag) { ((Graphic)image).color = SelectedTint; ((Graphic)val6).color = SelectedText; val6.fontStyle = (FontStyles)1; } string key = category.Key; ((UnityEvent)button.onClick).AddListener((UnityAction)delegate { CraftFilter.SelectCategory(key); }); val.AddComponent<RightClickHandler>().OnRightClick = delegate { CraftFilter.MarkCategorySeen(key); }; _buttons.Add(val); } } private static string Signature(List<CategoryEntry> categories, string selectedKey) { StringBuilder stringBuilder = new StringBuilder(selectedKey); foreach (CategoryEntry category in categories) { stringBuilder.Append('|').Append(category.Key).Append(':') .Append(category.Count) .Append('+') .Append(category.NewCount); } return stringBuilder.ToString(); } private void UpdateCraftableVisual() { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0029: 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_004a: 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) bool craftableOnly = CraftFilter.CraftableOnly; ((Graphic)_craftableImage).color = (craftableOnly ? SelectedTint : (Object.op_Implicit((Object)(object)_templateImage) ? ((Graphic)_templateImage).color : Color.white)); ((Graphic)_craftableLabel).color = (craftableOnly ? SelectedText : Color.white); _craftableLabel.text = (craftableOnly ? "<b>" : "") + Loc.CraftableOnly + (craftableOnly ? "</b>" : ""); } } internal class RightClickHandler : MonoBehaviour, IPointerClickHandler, IEventSystemHandler { public Action OnRightClick; 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 == 1) { OnRightClick?.Invoke(); } } } internal sealed class CategoryEntry { public string Key; public string Name; public int Count; public int NewCount; public ModSource Source; } [HarmonyPatch] internal static class CraftFilter { public const string AllKey = "*"; public static string SelectedKey = "*"; public static string SearchText = ""; public static bool CraftableOnly; public static readonly List<CategoryEntry> Categories = new List<CategoryEntry>(); public static readonly List<Recipe> LastUnfiltered = new List<Recipe>(); private static readonly Dictionary<string, string> SelectedByStation = new Dictionary<string, string>(); private static string _stationKey; private static readonly List<ItemData> TempItems = new List<ItemData>(); private static readonly Dictionary<string, int> TempCounts = new Dictionary<string, int>(); private static readonly Dictionary<string, int> TempNewCounts = new Dictionary<string, int>(); private static readonly Dictionary<string, ModSource> TempSources = new Dictionary<string, ModSource>(); private static bool _loggedOwners; public static void SelectCategory(string key) { SelectedKey = key; if (_stationKey != null) { SelectedByStation[_stationKey] = key; } Refresh(scrollToTop: true); } public static void SetSearch(string text) { text = text ?? ""; if (!(text == SearchText)) { SearchText = text; Refresh(scrollToTop: true); } } public static void SetCraftableOnly(bool value) { CraftableOnly = value; Refresh(scrollToTop: true); } public static void MarkCategorySeen(string key) { if (NewRecipes.MarkSeen(LastUnfiltered.Where((Recipe r) => key == "*" || ModOwnership.Resolve(r).Key == key).ToList())) { Refresh(scrollToTop: false); } } public static void Refresh(bool scrollToTop) { InventoryGui instance = InventoryGui.instance; if (Object.op_Implicit((Object)(object)instance) && Object.op_Implicit((Object)(object)Player.m_localPlayer) && InventoryGui.IsVisible()) { instance.UpdateCraftingPanel(false); if (scrollToTop && Object.op_Implicit((Object)(object)instance.m_recipeListScroll)) { instance.m_recipeListScroll.value = 1f; } } } [HarmonyPatch(typeof(InventoryGui), "Show")] [HarmonyPostfix] private static void ShowPostfix(InventoryGui __instance) { if (Plugin.Enabled.Value) { CategoryPanel.Ensure(__instance); } } [HarmonyPatch(typeof(InventoryGui), "Hide")] [HarmonyPostfix] private static void HidePostfix() { CategoryPanel.Instance?.OnInventoryHidden(); if (Plugin.ResetSearchOnClose.Value) { SearchText = ""; CategoryPanel.Instance?.ClearSearchField(); } } [HarmonyPatch(typeof(InventoryGui), "UpdateRecipeList")] [HarmonyPrefix] [HarmonyPriority(800)] [HarmonyBefore(new string[] { "local.valheim.craftsearch" })] private static void UpdateRecipeListPrefix(InventoryGui __instance, List<Recipe> recipes) { if (!Plugin.Enabled.Value || recipes == null) { return; } Player player = Player.m_localPlayer; if (!Object.op_Implicit((Object)(object)player)) { return; } try { CategoryPanel.Ensure(__instance); ModOwnership.RefreshIndex(); CraftingStation currentCraftingStation = player.GetCurrentCraftingStation(); OnStationChanged(currentCraftingStation); bool craftTab = __instance.InCraftTab(); BuildCategories(recipes, player, currentCraftingStation, craftTab); LogOwnersOnce(recipes); bool noCost = Object.op_Implicit((Object)(object)ZoneSystem.instance) && ZoneSystem.instance.GetGlobalKey((GlobalKeys)25); string[] terms = SplitTerms(SearchText); recipes.RemoveAll((Recipe recipe) => !MatchesCategory(recipe) || !MatchesSearch(recipe, terms) || (CraftableOnly && craftTab && !noCost && !player.HaveRequirements(recipe, false, 1, 1))); CategoryPanel.Instance?.SetCategories(Categories, SelectedKey); } catch (Exception arg) { Plugin.Log.LogError((object)$"Filtering recipes failed: {arg}"); } } [HarmonyPatch(typeof(InventoryGui), "UpdateRecipeList")] [HarmonyPostfix] private static void UpdateRecipeListPostfix(InventoryGui __instance) { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_01af: Unknown result type (might be due to invalid IL or missing references) //IL_01c4: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.Enabled.Value) { return; } try { bool flag = __instance.InCraftTab(); bool flag2 = NewRecipes.Enabled && flag; bool flag3 = Plugin.ShowModTagInAll.Value && SelectedKey == "*"; if (!flag2 && !flag3) { return; } foreach (RecipeDataPair availableRecipe in __instance.m_availableRecipes) { RecipeDataPair current = availableRecipe; if (!Object.op_Implicit((Object)(object)((RecipeDataPair)(ref current)).InterfaceElement)) { continue; } Transform obj = ((RecipeDataPair)(ref current)).InterfaceElement.transform.Find("name"); TMP_Text val = ((obj != null) ? ((Component)obj).GetComponent<TMP_Text>() : null); if (!Object.op_Implicit((Object)(object)val)) { continue; } if (flag2 && NewRecipes.IsNew(((RecipeDataPair)(ref current)).Recipe)) { val.text = "<size=75%><color=#8CFF5A><b>" + Loc.New + "</b></color></size> " + val.text; } if (flag3) { ModSource modSource = ModOwnership.Resolve(((RecipeDataPair)(ref current)).Recipe); if (modSource.Key != "valheim") { val.text = val.text + " <size=70%><color=#C8A26B>· " + modSource.Name + "</color></size>"; } } } if (flag2 && Plugin.SortNewFirst.Value) { List<RecipeDataPair> list = __instance.m_availableRecipes.OrderBy((RecipeDataPair p) => (!NewRecipes.IsNew(((RecipeDataPair)(ref p)).Recipe)) ? 1 : 0).ToList(); __instance.m_availableRecipes.Clear(); __instance.m_availableRecipes.AddRange(list); for (int num = 0; num < list.Count; num++) { RecipeDataPair val2 = list[num]; ((RectTransform)((RecipeDataPair)(ref val2)).InterfaceElement.transform).anchoredPosition = new Vector2(0f, (float)num * (0f - __instance.m_recipeListSpace)); } } } catch (Exception ex) { Plugin.Log.LogDebug((object)("Mod tags failed: " + ex.Message)); } } private static void OnStationChanged(CraftingStation station) { string text = (Object.op_Implicit((Object)(object)station) ? station.m_name : "$hand"); if (!(text == _stationKey)) { _stationKey = text; if (Plugin.RememberPerStation.Value) { SelectedKey = (SelectedByStation.TryGetValue(text, out var value) ? value : "*"); } } } private static bool VisibleInTab(Recipe recipe, Player player, CraftingStation station, bool craftTab) { if (!Object.op_Implicit((Object)(object)recipe) || !Object.op_Implicit((Object)(object)recipe.m_item)) { return false; } if (craftTab) { return !recipe.m_noCraftOnlyUpgrade; } SharedData shared = recipe.m_item.m_itemData.m_shared; if (shared.m_maxQuality <= 1) { return false; } bool flag = Object.op_Implicit((Object)(object)station) && station.m_upgrader; TempItems.Clear(); ((Humanoid)player).GetInventory().GetAllItems(shared.m_name, TempItems); foreach (ItemData tempItem in TempItems) { if (flag || tempItem.m_quality < tempItem.m_shared.m_maxQuality) { return true; } } return false; } private static void BuildCategories(List<Recipe> recipes, Player player, CraftingStation station, bool craftTab) { TempCounts.Clear(); TempNewCounts.Clear(); TempSources.Clear(); LastUnfiltered.Clear(); int num = 0; int num2 = 0; foreach (Recipe recipe in recipes) { if (VisibleInTab(recipe, player, station, craftTab)) { ModSource modSource = ModOwnership.Resolve(recipe); TempSources[modSource.Key] = modSource; TempCounts.TryGetValue(modSource.Key, out var value); TempCounts[modSource.Key] = value + 1; num++; LastUnfiltered.Add(recipe); if (craftTab && NewRecipes.IsNew(recipe)) { TempNewCounts.TryGetValue(modSource.Key, out var value2); TempNewCounts[modSource.Key] = value2 + 1; num2++; } } } if (SelectedKey != "*" && !TempCounts.ContainsKey(SelectedKey)) { ModSource modSource2 = Categories.FirstOrDefault((CategoryEntry c) => c.Key == SelectedKey)?.Source; if (modSource2 != null) { TempSources[modSource2.Key] = modSource2; TempCounts[modSource2.Key] = 0; } else { SelectedKey = "*"; } } Categories.Clear(); Categories.Add(new CategoryEntry { Key = "*", Name = Loc.All, Count = num, NewCount = num2 }); foreach (ModSource item in TempSources.Values.OrderBy((ModSource s) => s.Order).ThenBy<ModSource, string>((ModSource s) => s.Name, StringComparer.OrdinalIgnoreCase)) { Categories.Add(new CategoryEntry { Key = item.Key, Name = item.Name, Count = TempCounts[item.Key], NewCount = (TempNewCounts.TryGetValue(item.Key, out var value3) ? value3 : 0), Source = item }); } } private static bool MatchesCategory(Recipe recipe) { if (!(SelectedKey == "*")) { return ModOwnership.Resolve(recipe).Key == SelectedKey; } return true; } private static string[] SplitTerms(string text) { if (string.IsNullOrWhiteSpace(text)) { return Array.Empty<string>(); } return text.ToLowerInvariant().Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries); } private static bool MatchesSearch(Recipe recipe, string[] terms) { if (terms.Length == 0) { return true; } if (!Object.op_Implicit((Object)(object)recipe) || !Object.op_Implicit((Object)(object)recipe.m_item)) { return false; } StringBuilder stringBuilder = new StringBuilder(); SharedData shared = recipe.m_item.m_itemData.m_shared; stringBuilder.Append(Localization.instance.Localize(shared.m_name)).Append('\n'); stringBuilder.Append(((Object)recipe.m_item).name).Append('\n'); if (Plugin.SearchIngredients.Value && recipe.m_resources != null) { Requirement[] resources = recipe.m_resources; foreach (Requirement val in resources) { if (Object.op_Implicit((Object)(object)val?.m_resItem)) { stringBuilder.Append(Localization.instance.Localize(val.m_resItem.m_itemData.m_shared.m_name)).Append('\n'); } } } string text = stringBuilder.ToString().ToLowerInvariant(); foreach (string value in terms) { if (!text.Contains(value)) { return false; } } return true; } private static void LogOwnersOnce(List<Recipe> recipes) { if (!Plugin.LogRecipeOwners.Value || _loggedOwners || !Object.op_Implicit((Object)(object)ObjectDB.instance)) { return; } _loggedOwners = true; StringBuilder stringBuilder = new StringBuilder("Recipe owners:\n"); foreach (IGrouping<string, Recipe> item in from r in ObjectDB.instance.m_recipes where Object.op_Implicit((Object)(object)r) && Object.op_Implicit((Object)(object)r.m_item) group r by ModOwnership.Resolve(r).Name into g orderby g.Key select g) { stringBuilder.AppendLine($" [{item.Key}] {item.Count()} recipes"); foreach (Recipe item2 in item) { stringBuilder.AppendLine(" " + ((Object)item2).name + " -> " + ((Object)item2.m_item).name); } } Plugin.Log.LogInfo((object)stringBuilder.ToString()); } } [HarmonyPatch] internal static class InputGuard { private static int _lastTypingFrame = -10; public static bool Blocking { get { if (Object.op_Implicit((Object)(object)CategoryPanel.Instance) && CategoryPanel.Instance.SearchFocused) { _lastTypingFrame = Time.frameCount; return true; } return Time.frameCount - _lastTypingFrame <= 1; } } [HarmonyPatch(typeof(ZInput), "TryGetButtonState")] [HarmonyPrefix] private static bool ButtonStatePrefix(string name, ref bool __result) { if (!Blocking || name == null || name.StartsWith("Mouse", StringComparison.Ordinal)) { return true; } __result = false; return false; } [HarmonyPatch(typeof(ZInput), "TryGetKeyStateLowLevel")] [HarmonyPrefix] private static bool KeyStatePrefix(KeyCode keyCode, ref bool __result) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) if (!Blocking || IsEditingKey(keyCode)) { return true; } __result = false; return false; } private static bool IsEditingKey(KeyCode key) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 //IL_0009: 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_0011: Invalid comparison between Unknown and I4 //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Invalid comparison between Unknown and I4 //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Invalid comparison between Unknown and I4 if ((int)key == 8 || (int)key == 127 || key - 305 <= 1) { return true; } if ((int)key >= 323) { return (int)key <= 329; } return false; } } internal static class Loc { private static string Lang { get { try { Localization instance = Localization.instance; return ((instance != null) ? instance.GetSelectedLanguage() : null) ?? "English"; } catch { return "English"; } } } public static string All => Pick("All", "Усі", "Все"); public static string OtherMods => Pick("Other mods", "Інші моди", "Другие моды"); public static string Search => Pick("Search...", "Пошук...", "Поиск..."); public static string CraftableOnly => Pick("Craftable only", "Лише доступні", "Только доступные"); public static string New => Pick("NEW", "НОВЕ", "НОВОЕ"); public static string Categories => Pick("Categories", "Категорії", "Категории"); private static string Pick(string en, string uk, string ru) { string lang = Lang; if (!(lang == "Ukrainian")) { if (lang == "Russian") { return ru; } return en; } return uk; } } internal sealed class ModSource { public readonly string Key; public readonly string Name; public readonly int Order; private readonly string _iconPath; private Sprite _icon; private bool _iconLoaded; public Sprite Icon { get { if (_iconLoaded) { return _icon; } _iconLoaded = true; _icon = ((Key == "valheim") ? ModOwnership.VanillaIcon() : LoadSprite(_iconPath)); return _icon; } } public ModSource(string key, string name, int order, string iconPath) { Key = key; Name = name; Order = order; _iconPath = iconPath; } private static Sprite LoadSprite(string path) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrEmpty(path) || !File.Exists(path)) { return null; } try { Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false); if (!ImageConversion.LoadImage(val, File.ReadAllBytes(path))) { return null; } ((Texture)val).wrapMode = (TextureWrapMode)1; return Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f)); } catch (Exception ex) { Plugin.Log.LogDebug((object)("Could not load icon " + path + ": " + ex.Message)); return null; } } } [HarmonyPatch] internal static class ModOwnership { public const string VanillaKey = "valheim"; public const string UnknownKey = "unknown"; private static HashSet<string> _vanillaRecipes; private static HashSet<string> _vanillaItems; private static readonly Dictionary<Recipe, ModSource> Cache = new Dictionary<Recipe, ModSource>(); private static readonly Dictionary<string, ModSource> Sources = new Dictionary<string, ModSource>(); private static readonly Dictionary<Recipe, string> RecipeToGuid = new Dictionary<Recipe, string>(); private static int _indexedRecipeCount = -1; private static int _indexedObjectDbRecipes = -1; private static readonly FieldInfo ModQueryRecipesField = AccessTools.Field(typeof(ModQuery), "Recipes"); public static ModSource Vanilla => GetSource("valheim"); public static ModSource Unknown => GetSource("unknown"); [HarmonyPatch(typeof(ObjectDB), "Awake")] [HarmonyPrefix] [HarmonyPriority(int.MaxValue)] private static void ObjectDBAwakePrefix(ObjectDB __instance) { TrySnapshot(__instance); } [HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")] [HarmonyPrefix] [HarmonyPriority(int.MaxValue)] private static void ObjectDBCopyPrefix(ObjectDB other) { TrySnapshot(other); } private static void TrySnapshot(ObjectDB db) { if (_vanillaRecipes != null || !Object.op_Implicit((Object)(object)db) || db.m_recipes == null || db.m_recipes.Count == 0) { return; } _vanillaRecipes = new HashSet<string>(); foreach (Recipe recipe in db.m_recipes) { if (Object.op_Implicit((Object)(object)recipe)) { _vanillaRecipes.Add(((Object)recipe).name); } } _vanillaItems = new HashSet<string>(); foreach (GameObject item in db.m_items) { if (Object.op_Implicit((Object)(object)item)) { _vanillaItems.Add(((Object)item).name); } } Plugin.Log.LogInfo((object)$"Vanilla snapshot: {_vanillaRecipes.Count} recipes, {_vanillaItems.Count} items"); } [HarmonyPatch(typeof(ZNetScene), "OnDestroy")] [HarmonyPostfix] private static void ClearOnLogout() { Cache.Clear(); RecipeToGuid.Clear(); _indexedRecipeCount = -1; _indexedObjectDbRecipes = -1; } public static ModSource Resolve(Recipe recipe) { if (!Object.op_Implicit((Object)(object)recipe)) { return Unknown; } if (Cache.TryGetValue(recipe, out var value)) { return value; } ModSource modSource = ResolveUncached(recipe); Cache[recipe] = modSource; return modSource; } private static ModSource ResolveUncached(Recipe recipe) { string text = (Object.op_Implicit((Object)(object)recipe.m_item) ? ((Object)recipe.m_item).name : null); bool flag = text != null && _vanillaItems != null && _vanillaItems.Contains(text); if (_vanillaRecipes != null && _vanillaRecipes.Contains(((Object)recipe).name) && (text == null || flag)) { return Vanilla; } if (RecipeToGuid.TryGetValue(recipe, out var value)) { return FromGuid(value); } ModSource modSource = FromJotunnRegistry(recipe); if (modSource != null) { return modSource; } if (text != null && !flag) { IModPrefab val = SafeGetPrefab(text); if (((val != null) ? val.SourceMod : null) != null) { return FromGuid(val.SourceMod.GUID); } } if (_vanillaRecipes == null) { if (!flag && text != null) { return Unknown; } return Vanilla; } if (!flag || !_vanillaRecipes.Contains(((Object)recipe).name)) { return Unknown; } return Vanilla; } private static IModPrefab SafeGetPrefab(string name) { try { return ModQuery.GetPrefab(name); } catch (Exception) { return null; } } private static ModSource FromJotunnRegistry(Recipe recipe) { try { foreach (CustomRecipe recipe2 in ModRegistry.GetRecipes()) { if (!((Object)(object)((recipe2 != null) ? recipe2.Recipe : null) == (Object)null) && ((CustomEntity)recipe2).SourceMod != null && ((Object)(object)recipe2.Recipe == (Object)(object)recipe || ((Object)recipe2.Recipe).name == ((Object)recipe).name)) { return FromGuid(((CustomEntity)recipe2).SourceMod.GUID); } } } catch (Exception ex) { Plugin.Log.LogDebug((object)("ModRegistry lookup failed: " + ex.Message)); } return null; } public static void RefreshIndex() { int num = (Object.op_Implicit((Object)(object)ObjectDB.instance) ? ObjectDB.instance.m_recipes.Count : 0); Dictionary<string, List<Recipe>> dictionary = ModQueryRecipesField?.GetValue(null) as Dictionary<string, List<Recipe>>; int num2 = 0; if (dictionary != null) { foreach (KeyValuePair<string, List<Recipe>> item in dictionary) { num2 += item.Value?.Count ?? 0; } } if (num2 == _indexedRecipeCount && num == _indexedObjectDbRecipes) { return; } _indexedRecipeCount = num2; _indexedObjectDbRecipes = num; RecipeToGuid.Clear(); Cache.Clear(); if (dictionary == null) { return; } foreach (KeyValuePair<string, List<Recipe>> item2 in dictionary) { if (item2.Value == null || item2.Key == "com.jotunn.jotunn") { continue; } foreach (Recipe item3 in item2.Value) { if (Object.op_Implicit((Object)(object)item3) && !RecipeToGuid.ContainsKey(item3)) { RecipeToGuid.Add(item3, item2.Key); } } } } private static ModSource FromGuid(string guid) { if (!string.IsNullOrEmpty(guid)) { return GetSource(guid); } return Unknown; } private static ModSource GetSource(string key) { if (Sources.TryGetValue(key, out var value)) { return value; } if (!(key == "valheim")) { if (key == "unknown") { value = new ModSource(key, Loc.OtherMods, 2, null); } else { string name = key; string iconPath = null; if (Chainloader.PluginInfos.TryGetValue(key, out var value2)) { name = value2.Metadata.Name; iconPath = FindIcon(value2.Location); } value = new ModSource(key, name, 1, iconPath); } } else { value = new ModSource(key, "Valheim", 0, null); } Sources[key] = value; return value; } private static string FindIcon(string dllPath) { try { if (string.IsNullOrEmpty(dllPath)) { return null; } string b = Path.GetFullPath(Paths.PluginPath).TrimEnd(Path.DirectorySeparatorChar); string directoryName = Path.GetDirectoryName(Path.GetFullPath(dllPath)); for (int i = 0; i < 4; i++) { if (string.IsNullOrEmpty(directoryName)) { break; } if (string.Equals(directoryName.TrimEnd(Path.DirectorySeparatorChar), b, StringComparison.OrdinalIgnoreCase)) { break; } string text = Path.Combine(directoryName, "icon.png"); if (File.Exists(text)) { return text; } directoryName = Path.GetDirectoryName(directoryName); } } catch (Exception) { } return null; } internal static Sprite VanillaIcon() { if (!Object.op_Implicit((Object)(object)ObjectDB.instance)) { return null; } GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("Hammer"); ItemDrop val = (Object.op_Implicit((Object)(object)itemPrefab) ? itemPrefab.GetComponent<ItemDrop>() : null); if (!Object.op_Implicit((Object)(object)val)) { return null; } return val.m_itemData.GetIcon(); } } [HarmonyPatch] internal static class NewRecipes { private const string SaveKey = "CraftingCategories.NewRecipes"; private static Player _owner; private static HashSet<string> _new = new HashSet<string>(); private static bool _inPanelUpdate; public static bool Enabled => Plugin.HighlightNew.Value; public static bool IsNew(Recipe recipe) { Player localPlayer = Player.m_localPlayer; if (!Enabled || !Object.op_Implicit((Object)(object)localPlayer) || !Object.op_Implicit((Object)(object)recipe) || !Object.op_Implicit((Object)(object)recipe.m_item)) { return false; } return For(localPlayer).Contains(recipe.m_item.m_itemData.m_shared.m_name); } public static bool MarkSeen(IEnumerable<Recipe> recipes) { Player localPlayer = Player.m_localPlayer; if (!Object.op_Implicit((Object)(object)localPlayer)) { return false; } HashSet<string> hashSet = For(localPlayer); bool flag = false; foreach (Recipe recipe in recipes) { if (Object.op_Implicit((Object)(object)recipe) && Object.op_Implicit((Object)(object)recipe.m_item)) { flag |= hashSet.Remove(recipe.m_item.m_itemData.m_shared.m_name); } } if (flag) { Save(localPlayer); } return flag; } private static HashSet<string> For(Player player) { if ((Object)(object)_owner == (Object)(object)player) { return _new; } _owner = player; _new = new HashSet<string>(); if (player.m_customData.TryGetValue("CraftingCategories.NewRecipes", out var value) && !string.IsNullOrEmpty(value)) { string[] array = value.Split(new char[1] { '\n' }, StringSplitOptions.RemoveEmptyEntries); foreach (string item in array) { _new.Add(item); } } return _new; } private static void Save(Player player) { HashSet<string> hashSet = For(player); if (hashSet.Count == 0) { player.m_customData.Remove("CraftingCategories.NewRecipes"); } else { player.m_customData["CraftingCategories.NewRecipes"] = string.Join("\n", hashSet); } } [HarmonyPatch(typeof(Player), "AddKnownRecipe")] [HarmonyPrefix] private static void AddKnownRecipePrefix(Player __instance, Recipe recipe, out bool __state) { __state = Object.op_Implicit((Object)(object)recipe) && Object.op_Implicit((Object)(object)recipe.m_item) && !__instance.m_knownRecipes.Contains(recipe.m_item.m_itemData.m_shared.m_name); } [HarmonyPatch(typeof(Player), "AddKnownRecipe")] [HarmonyPostfix] private static void AddKnownRecipePostfix(Player __instance, Recipe recipe, bool __state) { if (__state && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && For(__instance).Add(recipe.m_item.m_itemData.m_shared.m_name)) { Save(__instance); } } [HarmonyPatch(typeof(InventoryGui), "UpdateCraftingPanel")] [HarmonyPrefix] private static void UpdateCraftingPanelPrefix() { _inPanelUpdate = true; } [HarmonyPatch(typeof(InventoryGui), "UpdateCraftingPanel")] [HarmonyFinalizer] private static void UpdateCraftingPanelFinalizer() { _inPanelUpdate = false; } [HarmonyPatch(typeof(InventoryGui), "SetRecipe")] [HarmonyPostfix] private static void SetRecipePostfix(InventoryGui __instance, int index) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) if (!_inPanelUpdate && Enabled && index >= 0 && index < __instance.m_availableRecipes.Count) { Recipe[] array = new Recipe[1]; RecipeDataPair val = __instance.m_availableRecipes[index]; array[0] = ((RecipeDataPair)(ref val)).Recipe; if (MarkSeen((IEnumerable<Recipe>)(object)array)) { CategoryPanel.Instance?.RequestRefresh(); } } } } public enum PanelSide { Auto, Left, Right } public enum SearchMode { Auto, On, Off } [BepInPlugin("com.kuzuk.valheim.craftingcategories", "CraftingCategories", "1.0.1")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { public const string Guid = "com.kuzuk.valheim.craftingcategories"; public const string Name = "CraftingCategories"; public const string Version = "1.0.1"; internal const string WorkstationSearchGuid = "local.valheim.craftsearch"; internal static ManualLogSource Log; internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<PanelSide> Side; internal static ConfigEntry<float> PanelWidth; internal static ConfigEntry<float> OffsetX; internal static ConfigEntry<float> OffsetY; internal static ConfigEntry<float> ButtonHeight; internal static ConfigEntry<bool> ShowModIcons; internal static ConfigEntry<bool> ShowCounts; internal static ConfigEntry<SearchMode> Search; internal static ConfigEntry<bool> SearchIngredients; internal static ConfigEntry<bool> ResetSearchOnClose; internal static ConfigEntry<bool> RememberPerStation; internal static ConfigEntry<bool> ShowModTagInAll; internal static ConfigEntry<bool> LogRecipeOwners; internal static ConfigEntry<bool> HighlightNew; internal static ConfigEntry<bool> SortNewFirst; internal static bool SearchEnabled => Search.Value switch { SearchMode.On => true, SearchMode.Off => false, _ => !Chainloader.PluginInfos.ContainsKey("local.valheim.craftsearch"), }; private void Awake() { //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Expected O, but got Unknown //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Expected O, but got Unknown //IL_024d: Unknown result type (might be due to invalid IL or missing references) //IL_0253: Invalid comparison between Unknown and I4 //IL_026f: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "Enabled", true, "Show the category sidebar next to the crafting menu."); RememberPerStation = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "RememberCategoryPerStation", true, "Remember the last selected category separately for every crafting station."); ShowModTagInAll = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "ShowModTagInAllCategory", true, "In the 'All' category, append the source mod name to recipes added by mods."); Side = ((BaseUnityPlugin)this).Config.Bind<PanelSide>("2 - Layout", "PanelSide", PanelSide.Auto, "Which side of the crafting panel the sidebar is attached to. Auto picks the side with more free space."); PanelWidth = ((BaseUnityPlugin)this).Config.Bind<float>("2 - Layout", "PanelWidth", 230f, new ConfigDescription("Sidebar width.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(120f, 500f), Array.Empty<object>())); ButtonHeight = ((BaseUnityPlugin)this).Config.Bind<float>("2 - Layout", "ButtonHeight", 32f, new ConfigDescription("Height of one category button.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(20f, 64f), Array.Empty<object>())); OffsetX = ((BaseUnityPlugin)this).Config.Bind<float>("2 - Layout", "OffsetX", 0f, "Extra horizontal offset of the sidebar."); OffsetY = ((BaseUnityPlugin)this).Config.Bind<float>("2 - Layout", "OffsetY", 0f, "Extra vertical offset of the sidebar."); ShowModIcons = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Layout", "ShowModIcons", true, "Show the mod's Thunderstore icon (icon.png next to the DLL) on its category button."); ShowCounts = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Layout", "ShowCounts", true, "Show how many recipes each category contains at the current station."); Search = ((BaseUnityPlugin)this).Config.Bind<SearchMode>("3 - Search", "SearchField", SearchMode.Auto, "Search field at the top of the sidebar. Auto = enabled unless Workstation Search is installed (it has its own)."); SearchIngredients = ((BaseUnityPlugin)this).Config.Bind<bool>("3 - Search", "SearchInIngredients", false, "Also match recipes whose ingredients contain the search text."); ResetSearchOnClose = ((BaseUnityPlugin)this).Config.Bind<bool>("3 - Search", "ResetSearchOnClose", true, "Clear the search text when the inventory is closed."); HighlightNew = ((BaseUnityPlugin)this).Config.Bind<bool>("4 - New recipes", "HighlightNewRecipes", true, "Mark freshly discovered recipes with a NEW badge (and +N on category buttons) until you select them. Right-click a category button to mark all its recipes as seen."); SortNewFirst = ((BaseUnityPlugin)this).Config.Bind<bool>("4 - New recipes", "SortNewFirst", false, "Put new recipes at the top of the list."); LogRecipeOwners = ((BaseUnityPlugin)this).Config.Bind<bool>("9 - Debug", "LogRecipeOwners", false, "Write every recipe and the mod it was attributed to into the BepInEx log when a station is opened."); if ((int)SystemInfo.graphicsDeviceType == 4) { Log.LogInfo((object)"Dedicated server detected - CraftingCategories is client-only and stays inactive."); return; } ModQuery.Enable(); new Harmony("com.kuzuk.valheim.craftingcategories").PatchAll(typeof(Plugin).Assembly); ((BaseUnityPlugin)this).Config.SettingChanged += delegate { CategoryPanel.Instance?.OnConfigChanged(); }; } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }