Decompiled source of HammerSearch v1.0.0

HammerSearch.dll

Decompiled a month ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using GUIFramework;
using HarmonyLib;
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: AssemblyTitle("Hammer Search")]
[assembly: AssemblyDescription("Search Valheim build pieces by name or material.")]
[assembly: AssemblyFileVersion("1.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace HammerSearch;

internal static class ConsoleCommands
{
	[Serializable]
	[CompilerGenerated]
	private sealed class <>c
	{
		public static readonly <>c <>9 = new <>c();

		public static ConsoleOptionsFetcher <>9__2_0;

		internal List<string> <Register>b__2_0()
		{
			return new List<string> { "on", "off" };
		}
	}

	private const string PersistCommand = "hammersearch_persist";

	private static bool _registered;

	public static void Register()
	{
		//IL_001f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0050: Expected O, but got Unknown
		//IL_004b: Unknown result type (might be due to invalid IL or missing references)
		//IL_003d: 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_0048: Expected O, but got Unknown
		if (_registered)
		{
			return;
		}
		_registered = true;
		try
		{
			ConsoleEvent val = OnPersist;
			object obj = <>c.<>9__2_0;
			if (obj == null)
			{
				ConsoleOptionsFetcher val2 = () => new List<string> { "on", "off" };
				<>c.<>9__2_0 = val2;
				obj = (object)val2;
			}
			new ConsoleCommand("hammersearch_persist", "Hammer Search: keep the piece filter after the build menu closes. No argument toggles it; 'on' or 'off' sets it explicitly.", val, false, false, false, false, false, (ConsoleOptionsFetcher)obj, false, false, false);
			HammerSearchPlugin.Log.LogInfo((object)"Registered console command: hammersearch_persist");
		}
		catch (Exception ex)
		{
			HammerSearchPlugin.Log.LogError((object)("Failed to register console commands: " + ex));
		}
	}

	private static void OnPersist(ConsoleEventArgs args)
	{
		try
		{
			bool value;
			if (args.Length > 1)
			{
				if (!TryParseState(args[1], out value))
				{
					args.Context.AddString("Usage: hammersearch_persist [on|off]");
					return;
				}
			}
			else
			{
				value = !SearchConfig.KeepFilterOnClose.Value;
			}
			SearchConfig.KeepFilterOnClose.Value = value;
			if (!value && !Hud.IsPieceSelectionVisible() && SearchState.IsActive)
			{
				SearchState.ClearEverywhere();
				args.Context.AddString("Hammer Search: filter cleared.");
			}
			args.Context.AddString(value ? "Hammer Search: filter PERSISTS after the build menu closes." : "Hammer Search: filter CLEARS when the build menu closes.");
		}
		catch (Exception ex)
		{
			HammerSearchPlugin.Log.LogError((object)("Console command failed: " + ex));
		}
	}

	private static bool TryParseState(string raw, out bool value)
	{
		value = false;
		if (string.IsNullOrEmpty(raw))
		{
			return false;
		}
		switch (raw.ToLowerInvariant())
		{
		case "on":
		case "true":
		case "1":
		case "yes":
			value = true;
			return true;
		case "off":
		case "false":
		case "0":
		case "no":
			value = false;
			return true;
		default:
			return false;
		}
	}
}
[BepInPlugin("com.austinwitt.hammersearch", "HammerSearch", "1.0.0")]
[BepInProcess("valheim.exe")]
public sealed class HammerSearchPlugin : BaseUnityPlugin
{
	public const string PluginGuid = "com.austinwitt.hammersearch";

	public const string PluginName = "HammerSearch";

	public const string PluginVersion = "1.0.0";

	private Harmony _harmony;

	internal static ManualLogSource Log { get; private set; }

	private void Awake()
	{
		//IL_0038: Unknown result type (might be due to invalid IL or missing references)
		//IL_0042: Expected O, but got Unknown
		Log = ((BaseUnityPlugin)this).Logger;
		SearchConfig.Bind(((BaseUnityPlugin)this).Config);
		if (!SearchConfig.Enabled.Value)
		{
			Log.LogWarning((object)"HammerSearch is disabled in configuration; no patches applied.");
			return;
		}
		_harmony = new Harmony("com.austinwitt.hammersearch");
		_harmony.PatchAll(typeof(HammerSearchPlugin).Assembly);
		Log.LogInfo((object)"HammerSearch 1.0.0 loaded.");
	}

	private void OnDestroy()
	{
		Harmony harmony = _harmony;
		if (harmony != null)
		{
			harmony.UnpatchSelf();
		}
	}
}
[HarmonyPatch(typeof(PieceTable), "UpdateAvailable")]
internal static class PieceTableUpdateAvailablePatch
{
	private static void Postfix(PieceTable __instance)
	{
		SearchState.ApplyFilter(__instance);
	}
}
[HarmonyPatch(typeof(Player), "TakeInput")]
internal static class PlayerTakeInputPatch
{
	private static void Postfix(ref bool __result)
	{
		if (SearchFieldUI.IsFocused)
		{
			__result = false;
		}
	}
}
[HarmonyPatch(typeof(Terminal), "InitTerminal")]
internal static class TerminalInitTerminalPatch
{
	private static void Postfix()
	{
		ConsoleCommands.Register();
	}
}
[HarmonyPatch(typeof(Hud), "Awake")]
internal static class HudAwakePatch
{
	private static void Postfix(Hud __instance)
	{
		PieceIndex.Clear();
		SearchFieldUI.Create(__instance);
		SearchFieldController.Attach();
	}
}
[HarmonyPatch(typeof(Hud), "OnDestroy")]
internal static class HudOnDestroyPatch
{
	private static void Postfix()
	{
		SearchFieldUI.Destroy();
		PieceIndex.Clear();
	}
}
[HarmonyPatch(typeof(Hud), "TogglePieceSelection")]
internal static class HudTogglePieceSelectionPatch
{
	private static void Postfix(Hud __instance)
	{
		if (!SearchFieldUI.Exists)
		{
			SearchFieldUI.Create(__instance);
		}
		SearchFieldController.Attach();
		if (Hud.IsPieceSelectionVisible())
		{
			if (SearchConfig.FocusOnOpen.Value)
			{
				SearchFieldUI.Focus();
			}
			return;
		}
		SearchFieldUI.Blur();
		if (!SearchConfig.KeepFilterOnClose.Value)
		{
			SearchState.ClearEverywhere();
		}
	}
}
internal sealed class SearchFieldController : MonoBehaviour
{
	private static SearchFieldController _instance;

	public static void Attach()
	{
		if (!((Object)(object)_instance != (Object)null))
		{
			Hud instance = Hud.instance;
			if (!((Object)(object)instance == (Object)null))
			{
				_instance = ((Component)instance).gameObject.AddComponent<SearchFieldController>();
			}
		}
	}

	private void Update()
	{
		//IL_002f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0034: Unknown result type (might be due to invalid IL or missing references)
		if (!Hud.IsPieceSelectionVisible())
		{
			if (SearchFieldUI.IsFocused)
			{
				SearchFieldUI.Blur();
			}
			return;
		}
		if (SearchFieldUI.IsFocused)
		{
			if (Input.GetKeyDown((KeyCode)27))
			{
				SearchFieldUI.Blur();
			}
			return;
		}
		KeyboardShortcut value = SearchConfig.FocusKey.Value;
		if (((KeyboardShortcut)(ref value)).IsDown())
		{
			SearchFieldUI.Focus();
		}
	}

	private void OnDestroy()
	{
		if ((Object)(object)_instance == (Object)(object)this)
		{
			_instance = null;
		}
	}
}
internal static class EventSystemHelper
{
	public static void ClearSelection(GameObject owner)
	{
		EventSystem current = EventSystem.current;
		if (!((Object)(object)current == (Object)null) && ((Object)(object)owner == (Object)null || (Object)(object)current.currentSelectedGameObject == (Object)(object)owner))
		{
			current.SetSelectedGameObject((GameObject)null);
		}
	}
}
internal sealed class PieceEntry
{
	public string[] Names;

	public string[] Materials;

	public string Description;
}
internal static class PieceIndex
{
	private static readonly Dictionary<Piece, PieceEntry> Cache = new Dictionary<Piece, PieceEntry>();

	private static string _cachedLanguage;

	private static bool _cachedDescriptions;

	private static readonly List<string> ScratchNames = new List<string>(4);

	private static readonly List<string> ScratchMaterials = new List<string>(12);

	private static readonly StringBuilder Scratch = new StringBuilder(64);

	private const char TokenMarker = '$';

	public static void InvalidateIfStale()
	{
		string text = null;
		Localization instance = Localization.instance;
		if (instance != null)
		{
			text = instance.GetSelectedLanguage();
		}
		if (!(text == _cachedLanguage) || SearchConfig.MatchDescriptions.Value != _cachedDescriptions)
		{
			Cache.Clear();
			_cachedLanguage = text;
			_cachedDescriptions = SearchConfig.MatchDescriptions.Value;
		}
	}

	public static void Clear()
	{
		Cache.Clear();
	}

	public static PieceEntry Get(Piece piece)
	{
		if ((Object)(object)piece == (Object)null)
		{
			return null;
		}
		if (Cache.TryGetValue(piece, out var value))
		{
			return value;
		}
		PieceEntry pieceEntry = Build(piece);
		Cache[piece] = pieceEntry;
		return pieceEntry;
	}

	private static PieceEntry Build(Piece piece)
	{
		ScratchNames.Clear();
		ScratchMaterials.Clear();
		AddTerm(ScratchNames, Localize(piece.m_name));
		AddTerm(ScratchNames, StripToken(piece.m_name));
		AddTerm(ScratchNames, CleanPrefabName(((Object)(object)((Component)piece).gameObject != (Object)null) ? ((Object)((Component)piece).gameObject).name : null));
		Requirement[] resources = piece.m_resources;
		if (resources != null)
		{
			Requirement[] array = resources;
			foreach (Requirement val in array)
			{
				if (val != null && !((Object)(object)val.m_resItem == (Object)null))
				{
					ItemData itemData = val.m_resItem.m_itemData;
					if (itemData != null && itemData.m_shared != null)
					{
						AddTerm(ScratchMaterials, Localize(itemData.m_shared.m_name));
						AddTerm(ScratchMaterials, StripToken(itemData.m_shared.m_name));
					}
					if ((Object)(object)((Component)val.m_resItem).gameObject != (Object)null)
					{
						AddTerm(ScratchMaterials, CleanPrefabName(((Object)((Component)val.m_resItem).gameObject).name));
					}
				}
			}
		}
		string description = string.Empty;
		if (SearchConfig.MatchDescriptions.Value)
		{
			string text = Normalize(Localize(piece.m_description));
			if (text != null)
			{
				description = text;
			}
		}
		return new PieceEntry
		{
			Names = ScratchNames.ToArray(),
			Materials = ScratchMaterials.ToArray(),
			Description = description
		};
	}

	private static void AddTerm(List<string> into, string raw)
	{
		string text = Normalize(raw);
		if (text != null && !into.Contains(text))
		{
			into.Add(text);
		}
	}

	private static string Localize(string token)
	{
		if (string.IsNullOrEmpty(token))
		{
			return null;
		}
		Localization instance = Localization.instance;
		if (instance == null)
		{
			return null;
		}
		string text = instance.Localize(token);
		if (string.IsNullOrEmpty(text))
		{
			return null;
		}
		if (text.IndexOf('$') >= 0)
		{
			return null;
		}
		return text;
	}

	private static string StripToken(string token)
	{
		if (string.IsNullOrEmpty(token) || token[0] != '$')
		{
			return null;
		}
		return token.Substring(1).Replace('_', ' ');
	}

	private static string CleanPrefabName(string name)
	{
		if (string.IsNullOrEmpty(name))
		{
			return null;
		}
		int num = name.IndexOf("(Clone)", StringComparison.Ordinal);
		if (num >= 0)
		{
			name = name.Substring(0, num);
		}
		return name.Replace('_', ' ').Replace('-', ' ');
	}

	private static string Normalize(string text)
	{
		if (string.IsNullOrEmpty(text))
		{
			return null;
		}
		Scratch.Length = 0;
		bool flag = false;
		foreach (char c in text)
		{
			switch (c)
			{
			case '<':
				flag = true;
				continue;
			case '>':
				flag = false;
				continue;
			}
			if (!flag)
			{
				Scratch.Append(char.ToLowerInvariant(c));
			}
		}
		string text2 = Scratch.ToString().Trim();
		if (text2.Length != 0)
		{
			return text2;
		}
		return null;
	}
}
internal static class SearchConfig
{
	public static ConfigEntry<bool> Enabled;

	public static ConfigEntry<bool> MatchMaterials;

	public static ConfigEntry<bool> MatchDescriptions;

	public static ConfigEntry<bool> AutoSwitchCategory;

	public static ConfigEntry<bool> KeepFilterOnClose;

	public static ConfigEntry<KeyboardShortcut> FocusKey;

	public static ConfigEntry<bool> FocusOnOpen;

	public static ConfigEntry<float> FieldWidth;

	public static ConfigEntry<float> FieldHeight;

	public static ConfigEntry<float> OffsetX;

	public static ConfigEntry<float> OffsetY;

	public static void Bind(ConfigFile cfg)
	{
		//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
		Enabled = cfg.Bind<bool>("General", "Enabled", true, "Master switch. When false the mod applies no patches at all.");
		MatchMaterials = cfg.Bind<bool>("Search", "MatchMaterials", true, "Also match a piece when the typed text matches one of its build materials, so typing 'stone' lists everything built from stone. Name matches always rank first.");
		MatchDescriptions = cfg.Bind<bool>("Search", "MatchDescriptions", false, "Also match against the piece description. Widens results considerably; off by default.");
		AutoSwitchCategory = cfg.Bind<bool>("Search", "AutoSwitchCategory", true, "When the current build category has no matches, jump to the first category that does. Without this you have to click through the tabs to find where your matches landed.");
		KeepFilterOnClose = cfg.Bind<bool>("Search", "KeepFilterOnClose", true, "Keep the filter after the build menu closes, so reopening shows the same filtered list and piece cycling stays within the matches. Turn it off to have the search reset every time the menu closes. Toggle in-game from the F5 console with: hammersearch_persist");
		FocusKey = cfg.Bind<KeyboardShortcut>("Input", "FocusKey", new KeyboardShortcut((KeyCode)102, (KeyCode[])(object)new KeyCode[1] { (KeyCode)306 }), "Pressed while the build menu is open, puts the caret in the search field. Press Escape to leave the field.");
		FocusOnOpen = cfg.Bind<bool>("Input", "FocusOnOpen", false, "Focus the search field automatically whenever the build menu opens. Convenient for typing, but it swallows movement keys until you press Escape.");
		FieldWidth = cfg.Bind<float>("Layout", "FieldWidth", 280f, "Width of the search field, in pixels.");
		FieldHeight = cfg.Bind<float>("Layout", "FieldHeight", 32f, "Height of the search field, in pixels.");
		OffsetX = cfg.Bind<float>("Layout", "OffsetX", 0f, "Horizontal nudge from the default position (top of the build menu). Use this if another UI mod overlaps the field.");
		OffsetY = cfg.Bind<float>("Layout", "OffsetY", 0f, "Vertical nudge from the default position.");
	}
}
internal static class SearchFieldUI
{
	private static GameObject _root;

	private static GuiInputField _input;

	private static GameObject _clearButton;

	private static readonly Color FieldBackground = new Color(0f, 0f, 0f, 0.65f);

	private static readonly Color FieldText = new Color(1f, 0.95f, 0.85f, 1f);

	private static readonly Color PlaceholderText = new Color(1f, 0.95f, 0.85f, 0.4f);

	public static bool IsFocused
	{
		get
		{
			if ((Object)(object)_input != (Object)null && (Object)(object)_root != (Object)null && _root.activeInHierarchy)
			{
				return ((TMP_InputField)_input).isFocused;
			}
			return false;
		}
	}

	public static bool Exists => (Object)(object)_root != (Object)null;

	public static void Create(Hud hud)
	{
		if ((Object)(object)hud == (Object)null || (Object)(object)hud.m_pieceSelectionWindow == (Object)null || (Object)(object)_root != (Object)null)
		{
			return;
		}
		try
		{
			Build(hud);
		}
		catch (Exception ex)
		{
			HammerSearchPlugin.Log.LogError((object)("Failed to build the search field: " + ex));
			Destroy();
		}
	}

	public static void Destroy()
	{
		if ((Object)(object)_root != (Object)null)
		{
			Object.Destroy((Object)(object)_root);
		}
		_root = null;
		_input = null;
		_clearButton = null;
	}

	public static void Focus()
	{
		if (!((Object)(object)_input == (Object)null) && !((Object)(object)_root == (Object)null) && _root.activeInHierarchy)
		{
			((Selectable)_input).Select();
			_input.ActivateInputField();
		}
	}

	public static void Blur()
	{
		if (!((Object)(object)_input == (Object)null))
		{
			((TMP_InputField)_input).DeactivateInputField(false);
			EventSystemHelper.ClearSelection(((Component)_input).gameObject);
		}
	}

	public static void SetText(string text)
	{
		if (!((Object)(object)_input == (Object)null))
		{
			((TMP_InputField)_input).text = text ?? string.Empty;
		}
	}

	public static void SyncClearButton()
	{
		if (!((Object)(object)_clearButton == (Object)null) && !((Object)(object)_input == (Object)null))
		{
			bool flag = ((TMP_InputField)_input).text.Length > 0;
			if (_clearButton.activeSelf != flag)
			{
				_clearButton.SetActive(flag);
			}
		}
	}

	private static void Build(Hud hud)
	{
		//IL_003a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0044: Expected O, but got Unknown
		//IL_0059: Unknown result type (might be due to invalid IL or missing references)
		//IL_005f: Expected O, but got Unknown
		//IL_0072: Unknown result type (might be due to invalid IL or missing references)
		//IL_0087: Unknown result type (might be due to invalid IL or missing references)
		//IL_009c: 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_00e0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
		//IL_011f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0124: Unknown result type (might be due to invalid IL or missing references)
		//IL_012a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0131: Expected O, but got Unknown
		//IL_0142: 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_0164: Unknown result type (might be due to invalid IL or missing references)
		//IL_0180: 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_0240: Unknown result type (might be due to invalid IL or missing references)
		//IL_026e: Unknown result type (might be due to invalid IL or missing references)
		Transform transform = hud.m_pieceSelectionWindow.transform;
		RectTransform val = (RectTransform)(object)((transform is RectTransform) ? transform : null);
		if (!((Object)(object)val == (Object)null))
		{
			TMP_FontAsset val2 = ResolveFont(hud);
			_root = new GameObject("HammerSearchField", new Type[1] { typeof(RectTransform) });
			_root.SetActive(false);
			RectTransform val3 = (RectTransform)_root.transform;
			((Transform)val3).SetParent((Transform)(object)val, false);
			val3.anchorMin = new Vector2(0.5f, 1f);
			val3.anchorMax = new Vector2(0.5f, 1f);
			val3.pivot = new Vector2(0.5f, 0f);
			val3.sizeDelta = new Vector2(SearchConfig.FieldWidth.Value, SearchConfig.FieldHeight.Value);
			val3.anchoredPosition = new Vector2(SearchConfig.OffsetX.Value, 12f + SearchConfig.OffsetY.Value);
			Image val4 = _root.AddComponent<Image>();
			((Graphic)val4).color = FieldBackground;
			((Graphic)val4).raycastTarget = true;
			GameObject val5 = new GameObject("Viewport", new Type[1] { typeof(RectTransform) });
			RectTransform val6 = (RectTransform)val5.transform;
			((Transform)val6).SetParent((Transform)(object)val3, false);
			val5.AddComponent<RectMask2D>();
			val6.anchorMin = Vector2.zero;
			val6.anchorMax = Vector2.one;
			val6.offsetMin = new Vector2(10f, 4f);
			val6.offsetMax = new Vector2(0f - SearchConfig.FieldHeight.Value, -4f);
			TextMeshProUGUI textComponent = CreateLabel("Text", val6, val2, FieldText);
			TextMeshProUGUI val7 = CreateLabel("Placeholder", val6, val2, PlaceholderText);
			((TMP_Text)val7).text = "Search pieces or materials";
			_input = _root.AddComponent<GuiInputField>();
			_input.VirtualKeyboardTitle = "Search pieces";
			((TMP_InputField)_input).textViewport = val6;
			((TMP_InputField)_input).textComponent = (TMP_Text)(object)textComponent;
			((TMP_InputField)_input).placeholder = (Graphic)(object)val7;
			((TMP_InputField)_input).fontAsset = val2;
			((TMP_InputField)_input).pointSize = 16f;
			((TMP_InputField)_input).lineType = (LineType)0;
			((TMP_InputField)_input).richText = false;
			((TMP_InputField)_input).restoreOriginalTextOnEscape = false;
			((TMP_InputField)_input).caretColor = FieldText;
			((TMP_InputField)_input).customCaretColor = true;
			((TMP_InputField)_input).selectionColor = new Color(0.6f, 0.5f, 0.3f, 0.5f);
			((Selectable)_input).targetGraphic = (Graphic)(object)val4;
			((TMP_InputField)_input).text = SearchState.Text;
			((UnityEvent<string>)(object)((TMP_InputField)_input).onValueChanged).AddListener((UnityAction<string>)OnValueChanged);
			_clearButton = CreateClearButton(val3, val2);
			SyncClearButton();
			_root.SetActive(true);
			HammerSearchPlugin.Log.LogInfo((object)"Search field added to the build menu.");
		}
	}

	private static void OnValueChanged(string value)
	{
		try
		{
			SearchState.SetText(value);
			SyncClearButton();
		}
		catch (Exception ex)
		{
			HammerSearchPlugin.Log.LogError((object)("Search update failed: " + ex));
		}
	}

	private static GameObject CreateClearButton(RectTransform parent, TMP_FontAsset font)
	{
		//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_0034: Unknown result type (might be due to invalid IL or missing references)
		//IL_003a: Expected O, but got Unknown
		//IL_004d: 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_0077: Unknown result type (might be due to invalid IL or missing references)
		//IL_0084: 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_00a3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
		//IL_0108: Unknown result type (might be due to invalid IL or missing references)
		//IL_0122: Unknown result type (might be due to invalid IL or missing references)
		//IL_012c: Expected O, but got Unknown
		//IL_012d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0132: Unknown result type (might be due to invalid IL or missing references)
		//IL_0135: Unknown result type (might be due to invalid IL or missing references)
		//IL_0155: Unknown result type (might be due to invalid IL or missing references)
		//IL_0175: Unknown result type (might be due to invalid IL or missing references)
		//IL_017f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0186: Expected O, but got Unknown
		float num = SearchConfig.FieldHeight.Value - 8f;
		GameObject val = new GameObject("Clear", new Type[1] { typeof(RectTransform) });
		RectTransform val2 = (RectTransform)val.transform;
		((Transform)val2).SetParent((Transform)(object)parent, false);
		val2.anchorMin = new Vector2(1f, 0.5f);
		val2.anchorMax = new Vector2(1f, 0.5f);
		val2.pivot = new Vector2(1f, 0.5f);
		val2.sizeDelta = new Vector2(num, num);
		val2.anchoredPosition = new Vector2(-4f, 0f);
		Image val3 = val.AddComponent<Image>();
		((Graphic)val3).color = new Color(1f, 1f, 1f, 0.08f);
		((Graphic)val3).raycastTarget = true;
		TextMeshProUGUI obj = CreateLabel("X", val2, font, FieldText);
		((TMP_Text)obj).text = "×";
		((TMP_Text)obj).fontSize = 22f;
		((TMP_Text)obj).alignment = (TextAlignmentOptions)514;
		((Graphic)obj).raycastTarget = false;
		Button obj2 = val.AddComponent<Button>();
		((Selectable)obj2).targetGraphic = (Graphic)(object)val3;
		((UnityEvent)obj2.onClick).AddListener(new UnityAction(OnClearClicked));
		ColorBlock colors = ((Selectable)obj2).colors;
		((ColorBlock)(ref colors)).normalColor = Color.white;
		((ColorBlock)(ref colors)).highlightedColor = new Color(1f, 0.85f, 0.6f, 1f);
		((ColorBlock)(ref colors)).pressedColor = new Color(0.8f, 0.65f, 0.4f, 1f);
		((Selectable)obj2).colors = colors;
		return val;
	}

	private static void OnClearClicked()
	{
		try
		{
			SetText(string.Empty);
			Blur();
			SyncClearButton();
		}
		catch (Exception ex)
		{
			HammerSearchPlugin.Log.LogError((object)("Clearing the search failed: " + ex));
		}
	}

	private static TextMeshProUGUI CreateLabel(string name, RectTransform parent, TMP_FontAsset font, Color color)
	{
		//IL_0014: 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_001f: 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_002c: Unknown result type (might be due to invalid IL or missing references)
		//IL_002d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0037: Unknown result type (might be due to invalid IL or missing references)
		//IL_0038: 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_004d: 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)
		GameObject val = new GameObject(name, new Type[1] { typeof(RectTransform) });
		RectTransform val2 = (RectTransform)val.transform;
		((Transform)val2).SetParent((Transform)(object)parent, false);
		val2.anchorMin = Vector2.zero;
		val2.anchorMax = Vector2.one;
		val2.offsetMin = Vector2.zero;
		val2.offsetMax = Vector2.zero;
		TextMeshProUGUI val3 = val.AddComponent<TextMeshProUGUI>();
		if ((Object)(object)font != (Object)null)
		{
			((TMP_Text)val3).font = font;
		}
		((Graphic)val3).color = color;
		((TMP_Text)val3).fontSize = 16f;
		((TMP_Text)val3).alignment = (TextAlignmentOptions)513;
		((TMP_Text)val3).textWrappingMode = (TextWrappingModes)0;
		((TMP_Text)val3).overflowMode = (TextOverflowModes)0;
		((TMP_Text)val3).richText = false;
		return val3;
	}

	private static TMP_FontAsset ResolveFont(Hud hud)
	{
		if ((Object)(object)hud.m_buildSelection != (Object)null && (Object)(object)hud.m_buildSelection.font != (Object)null)
		{
			return hud.m_buildSelection.font;
		}
		if ((Object)(object)hud.m_pieceDescription != (Object)null && (Object)(object)hud.m_pieceDescription.font != (Object)null)
		{
			return hud.m_pieceDescription.font;
		}
		TMP_Text componentInChildren = ((Component)hud).GetComponentInChildren<TMP_Text>(true);
		if ((Object)(object)componentInChildren != (Object)null && (Object)(object)componentInChildren.font != (Object)null)
		{
			return componentInChildren.font;
		}
		HammerSearchPlugin.Log.LogWarning((object)"Could not find a font on the build HUD; the search field will use the TMP default.");
		return null;
	}
}
internal sealed class SearchQuery
{
	private const int NameExact = 0;

	private const int NamePrefix = 1;

	private const int NameWord = 2;

	private const int NameContains = 3;

	private const int DescriptionContains = 4;

	private const int MaterialExact = 10;

	private const int MaterialPrefix = 11;

	private const int MaterialWord = 12;

	private const int MaterialContains = 13;

	private const int NoMatch = int.MaxValue;

	private static readonly char[] Separators = new char[2] { ' ', '\t' };

	private static readonly char[] WordBreaks = new char[8] { ' ', '_', '-', ',', '.', '(', ')', '/' };

	private readonly string[] _terms;

	private readonly bool _namesOnly;

	private readonly bool _materialsOnly;

	public static readonly SearchQuery Empty = new SearchQuery(new string[0], namesOnly: false, materialsOnly: false);

	public bool IsEmpty => _terms.Length == 0;

	private SearchQuery(string[] terms, bool namesOnly, bool materialsOnly)
	{
		_terms = terms;
		_namesOnly = namesOnly;
		_materialsOnly = materialsOnly;
	}

	public static SearchQuery Parse(string raw)
	{
		if (string.IsNullOrEmpty(raw))
		{
			return Empty;
		}
		string text = raw.Trim().ToLowerInvariant();
		if (text.Length == 0)
		{
			return Empty;
		}
		bool namesOnly = false;
		bool materialsOnly = false;
		if (TryTakePrefix(ref text, "m:") || TryTakePrefix(ref text, "mat:"))
		{
			materialsOnly = true;
		}
		else if (TryTakePrefix(ref text, "n:") || TryTakePrefix(ref text, "name:"))
		{
			namesOnly = true;
		}
		string[] array = text.Split(Separators, StringSplitOptions.RemoveEmptyEntries);
		if (array.Length == 0)
		{
			return Empty;
		}
		return new SearchQuery(array, namesOnly, materialsOnly);
	}

	private static bool TryTakePrefix(ref string text, string prefix)
	{
		if (!text.StartsWith(prefix, StringComparison.Ordinal))
		{
			return false;
		}
		text = text.Substring(prefix.Length).Trim();
		return true;
	}

	public int Score(PieceEntry entry)
	{
		if (entry == null)
		{
			return int.MaxValue;
		}
		int num = 0;
		string[] terms = _terms;
		foreach (string term in terms)
		{
			int num2 = ScoreTerm(entry, term);
			if (num2 == int.MaxValue)
			{
				return int.MaxValue;
			}
			num += num2;
		}
		return num;
	}

	private int ScoreTerm(PieceEntry entry, string term)
	{
		int num = int.MaxValue;
		if (!_materialsOnly)
		{
			string[] names = entry.Names;
			for (int i = 0; i < names.Length; i++)
			{
				int num2 = ScoreField(names[i], term, 0, 1, 2, 3);
				if (num2 < num)
				{
					num = num2;
				}
			}
			if (num == int.MaxValue && entry.Description.Length > 0 && entry.Description.IndexOf(term, StringComparison.Ordinal) >= 0)
			{
				num = 4;
			}
		}
		if (!_namesOnly && SearchConfig.MatchMaterials.Value && num > 10)
		{
			string[] names = entry.Materials;
			for (int i = 0; i < names.Length; i++)
			{
				int num3 = ScoreField(names[i], term, 10, 11, 12, 13);
				if (num3 < num)
				{
					num = num3;
				}
			}
		}
		return num;
	}

	private static int ScoreField(string field, string term, int exact, int prefix, int word, int contains)
	{
		if (string.IsNullOrEmpty(field))
		{
			return int.MaxValue;
		}
		if (field.Length == term.Length && string.CompareOrdinal(field, term) == 0)
		{
			return exact;
		}
		if (field.StartsWith(term, StringComparison.Ordinal))
		{
			return prefix;
		}
		int num = field.IndexOf(term, StringComparison.Ordinal);
		if (num < 0)
		{
			return int.MaxValue;
		}
		if (Array.IndexOf(WordBreaks, field[num - 1]) < 0)
		{
			return contains;
		}
		return word;
	}

	public void Apply(List<Piece> pieces, List<ScoredPiece> scratch)
	{
		scratch.Clear();
		for (int i = 0; i < pieces.Count; i++)
		{
			Piece val = pieces[i];
			if (!((Object)(object)val == (Object)null))
			{
				int num = Score(PieceIndex.Get(val));
				if (num != int.MaxValue)
				{
					scratch.Add(new ScoredPiece(val, num, i));
				}
			}
		}
		scratch.Sort(ScoredPiece.Compare);
		pieces.Clear();
		foreach (ScoredPiece item in scratch)
		{
			pieces.Add(item.Piece);
		}
	}
}
internal struct ScoredPiece
{
	public readonly Piece Piece;

	public readonly int Score;

	public readonly int Order;

	public ScoredPiece(Piece piece, int score, int order)
	{
		Piece = piece;
		Score = score;
		Order = order;
	}

	public static int Compare(ScoredPiece a, ScoredPiece b)
	{
		int score;
		if (a.Score != b.Score)
		{
			score = a.Score;
			return score.CompareTo(b.Score);
		}
		score = a.Order;
		return score.CompareTo(b.Order);
	}
}
internal static class SearchState
{
	private static readonly FieldRef<PieceTable, List<List<Piece>>> AvailablePieces = AccessTools.FieldRefAccess<PieceTable, List<List<Piece>>>("m_availablePieces");

	private static readonly FieldRef<Player, PieceTable> BuildPieces = AccessTools.FieldRefAccess<Player, PieceTable>("m_buildPieces");

	private static readonly Action<Player> UpdateAvailablePiecesList = AccessTools.MethodDelegate<Action<Player>>(AccessTools.Method(typeof(Player), "UpdateAvailablePiecesList", (Type[])null, (Type[])null), (object)null, true);

	private static readonly List<ScoredPiece> Scratch = new List<ScoredPiece>(128);

	private static bool _refreshing;

	public static string Text { get; private set; } = string.Empty;

	public static SearchQuery Query { get; private set; } = SearchQuery.Empty;

	public static bool IsActive => !Query.IsEmpty;

	public static void SetText(string text)
	{
		text = text ?? string.Empty;
		if (!(text == Text))
		{
			Text = text;
			Query = SearchQuery.Parse(text);
			Refresh();
		}
	}

	public static void Refresh()
	{
		//IL_003b: Unknown result type (might be due to invalid IL or missing references)
		if (_refreshing)
		{
			return;
		}
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null)
		{
			return;
		}
		PieceTable val = BuildPieces.Invoke(localPlayer);
		if ((Object)(object)val == (Object)null)
		{
			return;
		}
		_refreshing = true;
		try
		{
			PieceIndex.InvalidateIfStale();
			try
			{
				val.SetSelected(Vector2Int.zero);
			}
			catch (Exception ex)
			{
				HammerSearchPlugin.Log.LogWarning((object)("Could not reset the piece cursor: " + ex.Message));
			}
			UpdateAvailablePiecesList(localPlayer);
			if (SearchConfig.AutoSwitchCategory.Value && IsActive)
			{
				SwitchToCategoryWithMatches(localPlayer, val);
			}
		}
		catch (Exception ex2)
		{
			HammerSearchPlugin.Log.LogError((object)("Failed to refresh the piece list: " + ex2));
		}
		finally
		{
			_refreshing = false;
		}
	}

	public static void ApplyFilter(PieceTable table)
	{
		if ((Object)(object)table == (Object)null || Query.IsEmpty)
		{
			return;
		}
		List<List<Piece>> list = AvailablePieces.Invoke(table);
		if (list == null)
		{
			return;
		}
		PieceIndex.InvalidateIfStale();
		foreach (List<Piece> item in list)
		{
			if (item != null && item.Count != 0)
			{
				Query.Apply(item, Scratch);
			}
		}
	}

	private static void SwitchToCategoryWithMatches(Player player, PieceTable table)
	{
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		//IL_0039: Unknown result type (might be due to invalid IL or missing references)
		List<PieceCategory> categories = table.m_categories;
		if (categories == null || categories.Count == 0 || table.GetAvailablePiecesInSelectedCategory() > 0)
		{
			return;
		}
		for (int i = 0; i < categories.Count; i++)
		{
			if (table.GetAvailablePiecesInCategory(categories[i]) > 0)
			{
				table.SetCategory(i);
				table.SetSelected(Vector2Int.zero);
				UpdateAvailablePiecesList(player);
				break;
			}
		}
	}

	public static void ClearEverywhere()
	{
		SetText(string.Empty);
		SearchFieldUI.SetText(string.Empty);
	}
}