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 Trove v0.1.1
plugins/Trove/Trove.dll
Decompiled 4 days agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; 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.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using TMPro; using Trove.Core; using Trove.Model; using Trove.UI; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("assembly_valheim")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("Trove")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Remembers where you gathered: a map pin for every patch you picked and every ore node you mined.")] [assembly: AssemblyFileVersion("0.1.1.0")] [assembly: AssemblyInformationalVersion("0.1.1+3e83e954bc3f34e5e62cf10969ce6ef3e4d43e20")] [assembly: AssemblyProduct("Trove")] [assembly: AssemblyTitle("Trove")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.1.1.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } internal sealed class ConfigurationManagerAttributes { public bool? IsAdvanced; public int? Order; public bool? Browsable; } namespace Trove { [BepInPlugin("com.jumpingmushroom.trove", "Trove", "0.1.1")] [BepInProcess("valheim.exe")] [BepInProcess("valheim.x86_64")] public sealed class TrovePlugin : BaseUnityPlugin { public const string PluginGuid = "com.jumpingmushroom.trove"; public const string PluginName = "Trove"; public const string PluginVersion = "0.1.1"; internal static ManualLogSource Log; private readonly ResourcePins _pins = new ResourcePins(); private readonly PatchHover _hover = new PatchHover(); private Harmony _harmony; private float _nextSave; private bool _hadPlayer; internal static bool InputBlocked() { if (Console.IsVisible()) { return true; } if (Menu.IsVisible()) { return true; } if (TextInput.IsVisible()) { return true; } if (Minimap.InTextInput()) { return true; } if ((Object)(object)Chat.instance != (Object)null && Chat.instance.HasFocus()) { return true; } return false; } private void Awake() { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; PluginConfig.Bind(((BaseUnityPlugin)this).Config); ConsoleCommands.Register(); _harmony = new Harmony("com.jumpingmushroom.trove"); _harmony.PatchAll(typeof(TrovePlugin).Assembly); PluginConfig.PinsChanged += OnPinsChanged; ((BaseUnityPlugin)this).Logger.LogInfo((object)"Trove 0.1.1 loaded."); } private void OnDestroy() { PluginConfig.PinsChanged -= OnPinsChanged; _pins.Clear(); _hover.Destroy(); PatchCache.Unload(); ResourceCatalog.Clear(); if (_harmony != null) { _harmony.UnpatchSelf(); } } private void OnPinsChanged() { _pins.Invalidate(); } private void LocalPlayerGone() { _pins.Clear(); _hover.Destroy(); PatchCache.Unload(); ResourceCatalog.Clear(); } private void Update() { //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_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Invalid comparison between Unknown and I4 Player localPlayer = Player.m_localPlayer; bool flag = (Object)(object)localPlayer != (Object)null; if (!flag && _hadPlayer) { LocalPlayerGone(); } _hadPlayer = flag; if (!((Object)(object)localPlayer == (Object)null)) { ResourceCatalog.EnsureBuilt(); PatchCache.EnsureLoaded(); KeyboardShortcut value = PluginConfig.ToggleKey.Value; if (((KeyboardShortcut)(ref value)).IsDown() && !InputBlocked()) { PluginConfig.PinsEnabled.Value = !PluginConfig.PinsEnabled.Value; } _pins.Sync(); Minimap instance = Minimap.instance; if ((Object)(object)instance != (Object)null && (int)instance.m_mode == 2 && PluginConfig.PinsEnabled.Value) { _hover.Update(instance); } else { _hover.Clear(); } if (Time.time >= _nextSave) { _nextSave = Time.time + 10f; PatchCache.SaveIfDirty(); } } } } public static class PluginConfig { public static ConfigEntry<bool> GatherEnabled; public static ConfigEntry<float> GatherClusterRadius; public static ConfigEntry<bool> SkipPlayerBases; public static ConfigEntry<string> ExcludedItems; public static ConfigEntry<bool> PinOneShot; public static ConfigEntry<bool> MineEnabled; public static ConfigEntry<float> MineClusterRadius; public static ConfigEntry<string> IgnoredDrops; public static ConfigEntry<string> ExcludedPrefabs; public static ConfigEntry<int> DepletedPercent; public static ConfigEntry<bool> PinsEnabled; public static ConfigEntry<bool> ShowOnMinimap; public static ConfigEntry<bool> ShowCounts; public static ConfigEntry<bool> HideChecked; public static ConfigEntry<bool> Toast; public static ConfigEntry<KeyboardShortcut> ToggleKey; public static ConfigEntry<bool> HoverEnabled; public static ConfigEntry<bool> ClickEnabled; public static ConfigEntry<bool> Verbose; public static event Action PinsChanged; private static ConfigurationManagerAttributes Attr(int order, bool advanced = false) { return new ConfigurationManagerAttributes { Order = order, IsAdvanced = advanced }; } public static void Bind(ConfigFile cfg) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Expected O, but got Unknown //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Expected O, but got Unknown //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Expected O, but got Unknown //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Expected O, but got Unknown //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Expected O, but got Unknown //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Expected O, but got Unknown //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Expected O, but got Unknown //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Expected O, but got Unknown //IL_01e3: Unknown result type (might be due to invalid IL or missing references) //IL_01ed: Expected O, but got Unknown //IL_021e: Unknown result type (might be due to invalid IL or missing references) //IL_0228: Expected O, but got Unknown //IL_0250: Unknown result type (might be due to invalid IL or missing references) //IL_025a: Expected O, but got Unknown //IL_0282: Unknown result type (might be due to invalid IL or missing references) //IL_028c: Expected O, but got Unknown //IL_02b4: Unknown result type (might be due to invalid IL or missing references) //IL_02be: Expected O, but got Unknown //IL_02e6: Unknown result type (might be due to invalid IL or missing references) //IL_02f0: Expected O, but got Unknown //IL_0318: Unknown result type (might be due to invalid IL or missing references) //IL_0322: Expected O, but got Unknown //IL_0332: Unknown result type (might be due to invalid IL or missing references) //IL_034e: Unknown result type (might be due to invalid IL or missing references) //IL_0358: 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_03b2: Unknown result type (might be due to invalid IL or missing references) //IL_03bc: Expected O, but got Unknown //IL_03e3: Unknown result type (might be due to invalid IL or missing references) //IL_03ed: Expected O, but got Unknown GatherEnabled = cfg.Bind<bool>("Gather", "Enabled", true, new ConfigDescription("Remember every wild pickable you pick: berries, mushrooms, thistle, dandelions and anything else that grows back. Only what you actually pick; nothing is scanned.", (AcceptableValueBase)null, new object[1] { Attr(100) })); GatherClusterRadius = cfg.Bind<float>("Gather", "ClusterRadius", 20f, new ConfigDescription("Picks of the same item within this many metres of a patch join it instead of making a new pin.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(2f, 100f), new object[1] { Attr(95) })); SkipPlayerBases = cfg.Bind<bool>("Gather", "SkipPlayerBases", true, new ConfigDescription("Ignore picks inside a workbench radius, so harvesting your own farm does not pin it.", (AcceptableValueBase)null, new object[1] { Attr(90) })); ExcludedItems = cfg.Bind<string>("Gather", "ExcludedItems", "Wood,Frostwood,Flint", new ConfigDescription("Comma-separated item prefab names never to pin. Branches and flint on the ground do grow back, but nobody wants a pin per stick.", (AcceptableValueBase)null, new object[1] { Attr(85) })); PinOneShot = cfg.Bind<bool>("Gather", "PinOneShot", false, new ConfigDescription("Also pin pickables that never grow back (wild seeds). Off: they are ignored.", (AcceptableValueBase)null, new object[1] { Attr(80) })); MineEnabled = cfg.Bind<bool>("Mine", "Enabled", true, new ConfigDescription("Remember every ore node you put a pickaxe into. The pin goes when the node is gone.", (AcceptableValueBase)null, new object[1] { Attr(70) })); MineClusterRadius = cfg.Bind<float>("Mine", "ClusterRadius", 12f, new ConfigDescription("Nodes yielding the same ore within this many metres share one pin.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 60f), new object[1] { Attr(65) })); IgnoredDrops = cfg.Bind<string>("Mine", "IgnoredDrops", "Stone,Grausten,Wood,Frostwood,Coal", new ConfigDescription("Comma-separated item prefab names that do not make a rock worth remembering. A node is an ore node when it drops anything not in this list. The defaults are the building materials that are everywhere: without them every Ashlands cliff and every Deep North stump is a deposit.", (AcceptableValueBase)null, new object[1] { Attr(60) })); ExcludedPrefabs = cfg.Bind<string>("Mine", "ExcludedPrefabs", "IceShard_*,Rock_destructible_test", new ConfigDescription("Comma-separated node prefab names never to pin, even when they yield something worth keeping. A trailing * matches any ending, so IceShard_* covers all six Deep North shards. Use this for things that are scattered everywhere rather than worth walking back to.", (AcceptableValueBase)null, new object[1] { Attr(58) })); DepletedPercent = cfg.Bind<int>("Mine", "DepletedPercent", 90, new ConfigDescription("Forget a deposit once this share of its pieces is mined. Copper and silver keep a few pieces below the dig limit, so 100 would never trigger.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(50, 100), new object[1] { Attr(55) })); PinsEnabled = cfg.Bind<bool>("Pins", "Enabled", true, new ConfigDescription("Show the remembered patches on the large map. These pins are never saved into the character file and never shared through the cartography table.", (AcceptableValueBase)null, new object[1] { Attr(50) })); ShowOnMinimap = cfg.Bind<bool>("Pins", "ShowOnMinimap", false, new ConfigDescription("Also show them on the small minimap in the corner.", (AcceptableValueBase)null, new object[1] { Attr(45) })); ShowCounts = cfg.Bind<bool>("Pins", "ShowCounts", true, new ConfigDescription("Append the number of plants or nodes in the patch to the pin name.", (AcceptableValueBase)null, new object[1] { Attr(40) })); HideChecked = cfg.Bind<bool>("Pins", "HideChecked", false, new ConfigDescription("Hide a patch entirely while everything in it is picked, instead of crossing it out.", (AcceptableValueBase)null, new object[1] { Attr(35) })); Toast = cfg.Bind<bool>("Pins", "Toast", true, new ConfigDescription("Show a message in the top-left when a new patch is remembered.", (AcceptableValueBase)null, new object[1] { Attr(30) })); ToggleKey = cfg.Bind<KeyboardShortcut>("Pins", "ToggleKey", KeyboardShortcut.Empty, new ConfigDescription("Hotkey that shows and hides the pins. Unbound by default.", (AcceptableValueBase)null, new object[1] { Attr(25) })); HoverEnabled = cfg.Bind<bool>("Pins", "Hover", true, new ConfigDescription("Hovering a patch on the large map shows what is in it, how much is ready and when the rest grows back.", (AcceptableValueBase)null, new object[1] { Attr(22) })); ClickEnabled = cfg.Bind<bool>("Pins", "Click", true, new ConfigDescription("Left-click a patch to cross it out by hand (clears itself when something regrows); right-click to forget it. A forgotten patch is not re-pinned by later picks until 'trove unforget'.", (AcceptableValueBase)null, new object[1] { Attr(20) })); Verbose = cfg.Bind<bool>("Logging", "Verbose", false, new ConfigDescription("Log every pick, hit and pin change to the BepInEx log.", (AcceptableValueBase)null, new object[1] { Attr(5, advanced: true) })); PinsEnabled.SettingChanged += delegate { Raise(); }; ShowOnMinimap.SettingChanged += delegate { Raise(); }; ShowCounts.SettingChanged += delegate { Raise(); }; HideChecked.SettingChanged += delegate { Raise(); }; } private static void Raise() { Action pinsChanged = PluginConfig.PinsChanged; if (pinsChanged == null) { return; } try { pinsChanged(); } catch (Exception ex) { TrovePlugin.Log.LogWarning((object)("config listener threw: " + ex)); } } } } namespace Trove.UI { internal sealed class MapPanel { private RectTransform _panel; private TMP_Text _text; private string _lastText; public bool Created => (Object)(object)_panel != (Object)null; public bool Create(Minimap map, string name) { //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Expected O, but got Unknown //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: 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) //IL_0175: 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_0195: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: Unknown result type (might be due to invalid IL or missing references) //IL_01be: 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) if ((Object)(object)_panel != (Object)null) { return true; } if ((Object)(object)map == (Object)null || (Object)(object)map.m_largeRoot == (Object)null || (Object)(object)map.m_biomeNameLarge == (Object)null) { return false; } GameObject val = new GameObject(name, new Type[3] { typeof(RectTransform), typeof(CanvasRenderer), typeof(Image) }); val.transform.SetParent(map.m_largeRoot.transform, false); ref RectTransform panel = ref _panel; Transform transform = val.transform; panel = (RectTransform)(object)((transform is RectTransform) ? transform : null); _panel.anchorMin = Vector2.zero; _panel.anchorMax = Vector2.zero; _panel.pivot = new Vector2(0f, 1f); Image component = val.GetComponent<Image>(); ((Graphic)component).color = new Color(0.05f, 0.04f, 0.03f, 0.82f); ((Graphic)component).raycastTarget = false; GameObject val2 = Object.Instantiate<GameObject>(((Component)map.m_biomeNameLarge).gameObject, (Transform)(object)_panel); ((Object)val2).name = "Text"; Component[] components = val2.GetComponents<Component>(); foreach (Component val3 in components) { if ((Object)(object)val3 != (Object)null && ((object)val3).GetType().Name == "Localize") { Object.Destroy((Object)(object)val3); } } _text = val2.GetComponent<TMP_Text>(); Transform transform2 = val2.transform; Transform obj = ((transform2 is RectTransform) ? transform2 : null); ((RectTransform)obj).anchorMin = Vector2.zero; ((RectTransform)obj).anchorMax = Vector2.one; ((RectTransform)obj).pivot = new Vector2(0f, 1f); ((RectTransform)obj).offsetMin = new Vector2(10f, 7f); ((RectTransform)obj).offsetMax = new Vector2(-10f, -7f); _text.alignment = (TextAlignmentOptions)257; _text.fontSize = 17f; _text.textWrappingMode = (TextWrappingModes)0; _text.richText = true; ((Graphic)_text).color = Color.white; ((Graphic)_text).raycastTarget = false; _text.text = ""; val.SetActive(false); return true; } public void ShowAtScreen(Minimap map, string content, Vector2 screen, float offset = 18f) { //IL_001f: 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) Vector2 local = default(Vector2); if (!((Object)(object)_panel == (Object)null) && RectTransformUtility.ScreenPointToLocalPointInRectangle((RectTransform)/*isinst with value type is only supported in some contexts*/, screen, (Camera)null, ref local)) { ShowAtRootLocal(map, content, local, offset); } } public void ShowAtRootLocal(Minimap map, string content, Vector2 local, float offset = 18f) { //IL_007f: 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_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: 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_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_panel == (Object)null)) { if (content != _lastText) { _lastText = content; _text.text = content; Vector2 preferredValues = _text.GetPreferredValues(content); _panel.sizeDelta = new Vector2(Mathf.Ceil(preferredValues.x) + 20f, Mathf.Ceil(preferredValues.y) + 14f); } Transform transform = map.m_largeRoot.transform; Rect rect = ((RectTransform)((transform is RectTransform) ? transform : null)).rect; float num = local.x - ((Rect)(ref rect)).xMin + offset; float num2 = local.y - ((Rect)(ref rect)).yMin - offset; if (num + _panel.sizeDelta.x > ((Rect)(ref rect)).width) { num = local.x - ((Rect)(ref rect)).xMin - offset - _panel.sizeDelta.x; } if (num2 - _panel.sizeDelta.y < 0f) { num2 = local.y - ((Rect)(ref rect)).yMin + offset + _panel.sizeDelta.y; } _panel.anchoredPosition = new Vector2(num, num2); if (!((Component)_panel).gameObject.activeSelf) { ((Component)_panel).gameObject.SetActive(true); } ((Transform)_panel).SetAsLastSibling(); } } public void Hide() { if ((Object)(object)_panel != (Object)null && ((Component)_panel).gameObject.activeSelf) { ((Component)_panel).gameObject.SetActive(false); } } public void Destroy() { if ((Object)(object)_panel != (Object)null) { Object.Destroy((Object)(object)((Component)_panel).gameObject); } _panel = null; _text = null; _lastText = null; } } internal sealed class PatchHover { private readonly MapPanel _panel = new MapPanel(); private Minimap _map; public static Patch Current { get; private set; } public void Update(Minimap map) { //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007a: 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_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)map == (Object)null) { Clear(); return; } if (map != _map) { _panel.Destroy(); _map = map; } if (!PluginConfig.HoverEnabled.Value && !PluginConfig.ClickEnabled.Value) { Clear(); return; } if (Minimap.InTextInput() || !ZInput.IsMouseActive() || PatchStore.Patches.Count == 0) { Clear(); return; } Vector3 pointerPosition = ZInput.pointerPosition; Vector3 val = map.ScreenToWorldPoint(pointerPosition); if (val == Vector3.zero) { Clear(); return; } Patch patch = (Current = PatchStore.NearestAny(val, map.PinInteractRadius)); if (patch == null || !PluginConfig.HoverEnabled.Value) { _panel.Hide(); } else if (_panel.Created || _panel.Create(map, "TroveHover")) { _panel.ShowAtScreen(map, Describe(patch), Vector2.op_Implicit(pointerPosition)); } } public void Clear() { Current = null; _panel.Hide(); } public void Destroy() { Current = null; _panel.Destroy(); _map = null; } private static string Describe(Patch p) { //IL_012f: 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) double num = PatchStore.Now(); StringBuilder stringBuilder = new StringBuilder(160); string value = ResourceCatalog.DisplayName(ResourceCatalog.ForItem(p.Item)); if (string.IsNullOrEmpty(value)) { value = p.Item; } stringBuilder.Append("<b>").Append(value).Append("</b>"); if (p.Kind == ResourceKind.Ore) { stringBuilder.Append('\n').Append(p.Count).Append((p.Count == 1) ? " node" : " nodes"); } else { int value2 = p.ReadyCount(num); stringBuilder.Append('\n').Append(value2).Append(" of ") .Append(p.Count) .Append(" ready"); double num2 = p.NextReadySec(num); if (num2 > num) { stringBuilder.Append(", next in ").Append(Span(num2 - num)); } if (p.Kind == ResourceKind.OneShot) { stringBuilder.Append("\n<alpha=#99>does not grow back"); } } if (p.ManualChecked) { stringBuilder.Append("\n<alpha=#99>crossed out by you"); } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null) { stringBuilder.Append("\n<alpha=#99>").Append(Dist(Utils.DistanceXZ(((Component)localPlayer).transform.position, p.Center))).Append(" from you"); } if (PluginConfig.ClickEnabled.Value) { stringBuilder.Append("\n<alpha=#66>left-click: cross out right-click: forget"); } return stringBuilder.ToString(); } private static string Span(double sec) { if (sec < 60.0) { return "under a minute"; } long num = (long)(sec / 60.0); if (num < 60) { return num + " min"; } long num2 = num / 60; num %= 60; if (num <= 0) { return num2 + " h"; } return num2 + " h " + num + " min"; } private static string Dist(float m) { if (!(m < 1000f)) { return (m / 1000f).ToString("0.0") + " km"; } return Mathf.RoundToInt(m) + " m"; } } internal sealed class ResourcePins { private static ResourcePins s_current; private readonly Dictionary<int, PinData> _pins = new Dictionary<int, PinData>(); private readonly List<int> _gone = new List<int>(); private Minimap _map; private int _version = -1; private double _nextReadyCheck; private bool _shown; private bool _forceRebuild; public ResourcePins() { s_current = this; } public static void InvalidateAll() { if (s_current != null) { s_current.Invalidate(); } } public void Invalidate() { _forceRebuild = true; } public void Clear() { _pins.Clear(); _map = null; _version = -1; _shown = false; } public void Sync() { //IL_01fc: Unknown result type (might be due to invalid IL or missing references) //IL_01e2: 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) Minimap instance = Minimap.instance; if ((Object)(object)instance == (Object)null) { Clear(); return; } if (instance != _map) { Clear(); _map = instance; } if (!PluginConfig.PinsEnabled.Value || !PatchCache.Loaded) { if (_shown) { RemoveAll(instance); } _shown = false; return; } double num = PatchStore.Now(); bool flag = num >= _nextReadyCheck; if (flag) { _nextReadyCheck = num + 30.0; } bool num2 = !_shown || _forceRebuild || PatchStore.Version != _version || flag; _shown = true; if (!num2) { return; } if (_forceRebuild) { RemoveAll(instance); _forceRebuild = false; } _version = PatchStore.Version; bool value = PluginConfig.ShowCounts.Value; bool value2 = PluginConfig.HideChecked.Value; HashSet<int> hashSet = new HashSet<int>(); IReadOnlyList<Patch> patches = PatchStore.Patches; for (int i = 0; i < patches.Count; i++) { Patch patch = patches[i]; if (patch.Count == 0) { continue; } int num3 = patch.ReadyCount(num); bool flag2 = patch.ManualChecked || (patch.Kind != ResourceKind.Ore && num3 == 0); if (flag2 && value2) { continue; } ResourceCatalog.Info info = ResourceCatalog.ForItem(patch.Item); string text = ResourceCatalog.DisplayName(info); if (string.IsNullOrEmpty(text)) { text = patch.Item; } if (value && patch.Count > 1) { text = text + " ×" + patch.Count; } hashSet.Add(patch.Id); if (_pins.TryGetValue(patch.Id, out var value3)) { if (value3.m_name != text) { instance.RemovePin(value3); value3 = null; } else { value3.m_pos = patch.Center; value3.m_checked = flag2; } } if (value3 == null) { value3 = CreatePin(instance, patch.Center, text, info?.Icon, flag2); _pins[patch.Id] = value3; } } _gone.Clear(); foreach (KeyValuePair<int, PinData> pin in _pins) { if (!hashSet.Contains(pin.Key)) { _gone.Add(pin.Key); } } for (int j = 0; j < _gone.Count; j++) { instance.RemovePin(_pins[_gone[j]]); _pins.Remove(_gone[j]); } instance.m_pinUpdateRequired = true; } private static PinData CreatePin(Minimap map, Vector3 pos, string name, Sprite icon, bool isChecked) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //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_001e: 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_002f: Unknown result type (might be due to invalid IL or missing references) //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) //IL_003c: 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_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Expected O, but got Unknown //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Expected O, but got Unknown if ((Object)(object)icon == (Object)null) { icon = map.GetSprite((PinType)3); } PinData val = new PinData { m_type = (PinType)8, m_name = (name ?? ""), m_pos = pos, m_icon = icon, m_save = false, m_checked = isChecked, m_ownerID = 0L }; if (val.m_name.Length > 0) { val.m_NamePinData = new PinNameData(val); } map.m_pins.Add(val); map.m_pinUpdateRequired = true; return val; } private void RemoveAll(Minimap map) { foreach (KeyValuePair<int, PinData> pin in _pins) { map.RemovePin(pin.Value); } _pins.Clear(); _version = -1; } } } namespace Trove.Patches { [HarmonyPatch(typeof(Pickable), "Interact")] public static class Pickable_Interact_Patch { private static void Prefix(Pickable __instance, Humanoid character) { if (PluginConfig.GatherEnabled.Value && !((Object)(object)__instance == (Object)null) && !((Object)(object)character == (Object)null) && (object)character == Player.m_localPlayer && !__instance.GetPicked() && __instance.GetEnabled != 0) { ZNetView nview = __instance.m_nview; if (!((Object)(object)nview == (Object)null) && nview.IsValid()) { Capture.Pick(__instance); } } } } [HarmonyPatch(typeof(Pickable), "SetPicked")] public static class Pickable_SetPicked_Patch { private static void Postfix(Pickable __instance, bool picked) { if (!((Object)(object)__instance == (Object)null) && PatchCache.Loaded) { Capture.ObservePickable(__instance, picked); } } } [HarmonyPatch(typeof(Pickable), "Awake")] public static class Pickable_Awake_Patch { private static void Postfix(Pickable __instance) { if (!((Object)(object)__instance == (Object)null) && PatchCache.Loaded) { ZNetView nview = __instance.m_nview; if (!((Object)(object)nview == (Object)null) && nview.IsValid()) { Capture.ObservePickable(__instance, __instance.GetPicked()); } } } } internal static class Capture { public static void Pick(Pickable pickable) { //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: 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_0120: 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) ResourceCatalog.EnsureBuilt(); PatchCache.EnsureLoaded(); if (!PatchCache.Loaded) { return; } string prefabName = Utils.GetPrefabName(((Component)pickable).gameObject); ResourceCatalog.Info info = ResourceCatalog.Get(prefabName); if (info == null || (info.Kind == ResourceKind.OneShot && !PluginConfig.PinOneShot.Value) || (info.Kind != ResourceKind.Pickable && info.Kind != ResourceKind.OneShot)) { return; } Vector3 position = ((Component)pickable).transform.position; if (ResourceCatalog.Split(PluginConfig.ExcludedItems.Value).Contains(info.Item)) { return; } if (PluginConfig.SkipPlayerBases.Value && (Object)(object)EffectArea.IsPointInsideArea(position, (Type)4, 0f) != (Object)null) { if (PluginConfig.Verbose.Value) { TrovePlugin.Log.LogDebug((object)("skipped " + info.Item + " inside a player base")); } return; } bool created; Patch patch = PatchStore.Record(info.Kind, info.Item, prefabName, position, pickable.m_respawnTimeMinutes, picked: true, PatchStore.Now(), PluginConfig.GatherClusterRadius.Value, out created); if (patch != null) { if (PluginConfig.Verbose.Value) { TrovePlugin.Log.LogDebug((object)string.Format("pick {0} at ({1:0},{2:0}) -> patch #{3} x{4}{5}", info.Item, position.x, position.z, patch.Id, patch.Count, created ? " (new)" : "")); } if (created) { Notify.NewPatch(info); } } } public static void ObservePickable(Pickable pickable, bool picked) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) Vector3 position = ((Component)pickable).transform.position; double pickedSecOrZero = 0.0; if (picked) { ZNetView nview = pickable.m_nview; if ((Object)(object)nview != (Object)null && nview.IsValid()) { long num = nview.GetZDO().GetLong(ZDOVars.s_pickedTime, 0L); if (num > 0) { pickedSecOrZero = (double)num / 10000000.0; } } } PatchStore.Observe(position, picked, pickedSecOrZero); } } internal static class Notify { public static void NewPatch(ResourceCatalog.Info info) { if (PluginConfig.Toast.Value) { MessageHud instance = MessageHud.instance; if (!((Object)(object)instance == (Object)null)) { instance.ShowMessage((MessageType)1, "Remembered: " + ResourceCatalog.DisplayName(info), 0, info.Icon, false, true); } } } } internal static class MapClick { public static Patch Target(Minimap map) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) if (!PluginConfig.ClickEnabled.Value || !PluginConfig.PinsEnabled.Value) { return null; } Patch current = PatchHover.Current; if (current == null) { return null; } Vector3 val = map.ScreenToWorldPoint(ZInput.pointerPosition); float pinInteractRadius = map.PinInteractRadius; if (map.GetClosestPin(val, pinInteractRadius, true) != null) { return null; } if (Utils.DistanceXZ(current.Center, val) > pinInteractRadius) { return null; } return current; } } [HarmonyPatch(typeof(Minimap), "OnMapLeftClick")] public static class Minimap_OnMapLeftClick_Patch { private static bool Prefix(Minimap __instance) { Patch patch = MapClick.Target(__instance); if (patch == null) { return true; } PatchStore.ToggleManualCheck(patch); __instance.m_pinUpdateRequired = true; return false; } } [HarmonyPatch(typeof(Minimap), "RemovePinUnderPointer")] public static class Minimap_RemovePinUnderPointer_Patch { private static bool Prefix(Minimap __instance) { Patch patch = MapClick.Target(__instance); if (patch == null) { return true; } if (PluginConfig.Verbose.Value) { TrovePlugin.Log.LogDebug((object)$"forgot patch #{patch.Id} {patch.Item} x{patch.Count}"); } PatchStore.Forget(patch); __instance.m_pinUpdateRequired = true; return false; } } [HarmonyPatch(typeof(Minimap), "OnMapDblClick")] public static class Minimap_OnMapDblClick_Patch { private static bool Prefix(Minimap __instance) { return MapClick.Target(__instance) == null; } } [HarmonyPatch(typeof(MineRock5), "Damage")] public static class MineRock5_Damage_Patch { private static void Prefix(MineRock5 __instance, HitData hit) { MineCapture.Hit(((Component)__instance).gameObject, hit, __instance.m_minToolTier); } } [HarmonyPatch(typeof(MineRock), "Damage")] public static class MineRock_Damage_Patch { private static void Prefix(MineRock __instance, HitData hit) { MineCapture.Hit(((Component)__instance).gameObject, hit, __instance.m_minToolTier); } } [HarmonyPatch(typeof(Destructible), "Damage")] public static class Destructible_Damage_Patch { private static void Prefix(Destructible __instance, HitData hit) { MineCapture.Hit(((Component)__instance).gameObject, hit, __instance.m_minToolTier); } } [HarmonyPatch(typeof(ZNetScene), "Destroy")] public static class ZNetScene_Destroy_Patch { private static void Prefix(GameObject go) { if (!((Object)(object)go == (Object)null) && PatchCache.Loaded) { MineCapture.Destroyed(go); } } } [HarmonyPatch(typeof(MineRock5), "LoadHealth")] public static class MineRock5_LoadHealth_Patch { private static void Postfix(MineRock5 __instance) { MineCapture.CheckDepleted(__instance); } } [HarmonyPatch(typeof(MineRock5), "RPC_SetAreaHealth")] public static class MineRock5_RPC_SetAreaHealth_Patch { private static void Postfix(MineRock5 __instance) { MineCapture.CheckDepleted(__instance); } } internal static class MineCapture { public static void CheckDepleted(MineRock5 rock) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)rock == (Object)null || !PatchCache.Loaded || rock.m_hitAreas == null || rock.m_hitAreas.Count == 0) { return; } Vector3 position = ((Component)rock).transform.position; if (PatchStore.Find(position) == null) { return; } int num = 0; for (int i = 0; i < rock.m_hitAreas.Count; i++) { if (rock.m_hitAreas[i].m_health <= 0f) { num++; } } int num2 = num * 100 / rock.m_hitAreas.Count; if (num2 >= PluginConfig.DepletedPercent.Value && PatchStore.NoteDestroyed(position) && PluginConfig.Verbose.Value) { TrovePlugin.Log.LogDebug((object)$"mined out ({num2}%): {Utils.GetPrefabName(((Component)rock).gameObject)} at ({position.x:0},{position.z:0})"); } } public static void Hit(GameObject go, HitData hit, int minToolTier) { //IL_0029: 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) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Invalid comparison between Unknown and I4 //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0093: 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_00f0: Unknown result type (might be due to invalid IL or missing references) if (!PluginConfig.MineEnabled.Value || hit == null || (Object)(object)go == (Object)null) { return; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || hit.m_attacker != ((Character)localPlayer).GetZDOID() || (int)hit.m_skill != 12 || !hit.CheckToolTier(minToolTier, false)) { return; } ResourceCatalog.EnsureBuilt(); PatchCache.EnsureLoaded(); if (!PatchCache.Loaded) { return; } string prefabName = Utils.GetPrefabName(go); ResourceCatalog.Info info = ResourceCatalog.Get(prefabName); if (info == null || info.Kind != ResourceKind.Ore) { return; } Vector3 position = go.transform.position; bool created; Patch patch = PatchStore.Record(ResourceKind.Ore, info.Item, prefabName, position, 0f, picked: false, PatchStore.Now(), PluginConfig.MineClusterRadius.Value, out created); if (patch != null) { if (PluginConfig.Verbose.Value) { TrovePlugin.Log.LogDebug((object)string.Format("hit {0} ({1}) at ({2:0},{3:0}) -> patch #{4} x{5}{6}", info.Item, prefabName, position.x, position.z, patch.Id, patch.Count, created ? " (new)" : "")); } if (created) { Notify.NewPatch(info); } } } public static void Destroyed(GameObject go) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: 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_007b: 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_00aa: Unknown result type (might be due to invalid IL or missing references) Vector3 position = go.transform.position; if (PatchStore.Find(position) != null) { Destructible component = go.GetComponent<Destructible>(); if ((!((Object)(object)component != (Object)null) || !((Object)(object)component.m_spawnWhenDestroyed != (Object)null) || !((Object)(object)component.m_spawnWhenDestroyed.GetComponent<MineRock5>() != (Object)null)) && (!((Object)(object)go.GetComponent<MineRock5>() == (Object)null) || !((Object)(object)go.GetComponent<MineRock>() == (Object)null) || !((Object)(object)component == (Object)null) || !((Object)(object)go.GetComponent<Pickable>() == (Object)null)) && PatchStore.NoteDestroyed(position) && PluginConfig.Verbose.Value) { TrovePlugin.Log.LogDebug((object)$"gone: {Utils.GetPrefabName(go)} at ({position.x:0},{position.z:0})"); } } } } [HarmonyPatch(typeof(Minimap), "LoadMapData")] public static class Minimap_LoadMapData_Patch { private static void Postfix() { ResourcePins.InvalidateAll(); } } [HarmonyPatch(typeof(Game), "SavePlayerProfile")] public static class Game_SavePlayerProfile_Patch { private static void Postfix() { PatchCache.SaveIfDirty(); } } } namespace Trove.Model { public enum ResourceKind { None, Pickable, OneShot, Ore } public sealed class Member { public string Key = ""; public Vector3 Pos; public string Prefab = ""; public float RespawnMinutes; public bool Picked; public double PickedSec; public double ReadySec { get { if (!Picked) { return 0.0; } return PickedSec + (double)RespawnMinutes * 60.0; } } public bool IsReady(double now) { if (!Picked) { return true; } if (RespawnMinutes <= 0f) { return false; } return now >= ReadySec; } public static string MakeKey(Vector3 p) { //IL_000a: 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_0042: Unknown result type (might be due to invalid IL or missing references) return string.Format(CultureInfo.InvariantCulture, "{0:0.0}|{1:0.0}|{2:0.0}", Mathf.Round(p.x * 2f) / 2f, Mathf.Round(p.y * 2f) / 2f, Mathf.Round(p.z * 2f) / 2f); } } public sealed class Patch { public int Id; public ResourceKind Kind; public string Item = ""; public readonly List<Member> Members = new List<Member>(); public bool ManualChecked; public Vector3 Center; public int Count => Members.Count; public int ReadyCount(double now) { int num = 0; for (int i = 0; i < Members.Count; i++) { if (Members[i].IsReady(now)) { num++; } } return num; } public double NextReadySec(double now) { double num = 0.0; for (int i = 0; i < Members.Count; i++) { Member member = Members[i]; if (member.Picked && !(member.RespawnMinutes <= 0f)) { double readySec = member.ReadySec; if (!(readySec <= now) && (num == 0.0 || readySec < num)) { num = readySec; } } } return num; } public void Recenter() { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: 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) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) if (Members.Count != 0) { Vector3 val = Vector3.zero; for (int i = 0; i < Members.Count; i++) { val += Members[i].Pos; } Center = val / (float)Members.Count; } } } } namespace Trove.Core { public static class ConsoleCommands { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static ConsoleEvent <>9__1_0; internal void <Register>b__1_0(ConsoleEventArgs args) { switch ((args.Length > 1) ? args[1].ToLowerInvariant() : "help") { case "list": List(args.Context); break; case "catalog": Catalog(args.Context); break; case "save": PatchCache.SaveIfDirty(); Say(args.Context, "Trove: saved to " + (PatchCache.Path ?? "(no world)")); break; case "unforget": PatchStore.Unforget(); PatchCache.SaveIfDirty(); Say(args.Context, "Trove: forgotten spots can be pinned again."); break; case "clear": if (args.Length > 2 && args[2].ToLowerInvariant() == "yes") { PatchStore.ForgetAll(); PatchCache.SaveIfDirty(); Say(args.Context, "Trove: forgot every patch for this world."); } else { Say(args.Context, "Trove: this wipes the world's cache. Type: trove clear yes"); } break; default: Say(args.Context, "trove list - every remembered patch: item, position, count, ready"); Say(args.Context, "trove catalog - what each prefab classifies as"); Say(args.Context, "trove save - write the cache now"); Say(args.Context, "trove unforget - let right-click-forgotten spots be pinned again"); Say(args.Context, "trove clear yes - forget everything for this world"); break; } } } private static bool _registered; public static void Register() { //IL_0041: 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_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Expected O, but got Unknown if (_registered) { return; } _registered = true; object obj = <>c.<>9__1_0; if (obj == null) { ConsoleEvent val = delegate(ConsoleEventArgs args) { switch ((args.Length > 1) ? args[1].ToLowerInvariant() : "help") { case "list": List(args.Context); break; case "catalog": Catalog(args.Context); break; case "save": PatchCache.SaveIfDirty(); Say(args.Context, "Trove: saved to " + (PatchCache.Path ?? "(no world)")); break; case "unforget": PatchStore.Unforget(); PatchCache.SaveIfDirty(); Say(args.Context, "Trove: forgotten spots can be pinned again."); break; case "clear": if (args.Length > 2 && args[2].ToLowerInvariant() == "yes") { PatchStore.ForgetAll(); PatchCache.SaveIfDirty(); Say(args.Context, "Trove: forgot every patch for this world."); } else { Say(args.Context, "Trove: this wipes the world's cache. Type: trove clear yes"); } break; default: Say(args.Context, "trove list - every remembered patch: item, position, count, ready"); Say(args.Context, "trove catalog - what each prefab classifies as"); Say(args.Context, "trove save - write the cache now"); Say(args.Context, "trove unforget - let right-click-forgotten spots be pinned again"); Say(args.Context, "trove clear yes - forget everything for this world"); break; } }; <>c.<>9__1_0 = val; obj = (object)val; } new ConsoleCommand("trove", "Trove: list | catalog | save | unforget | clear", (ConsoleEvent)obj, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); } private static void Say(Terminal ctx, string line) { ctx.AddString(line); TrovePlugin.Log.LogInfo((object)line); } private static void List(Terminal ctx) { double num = PatchStore.Now(); Say(ctx, string.Format("Trove: {0} patch(es), {1} member(s), {2} forgotten spot(s){3}", PatchStore.Patches.Count, PatchStore.MemberCount, PatchStore.ForgottenCount, PatchCache.Loaded ? "" : " (no world loaded)")); foreach (Patch patch in PatchStore.Patches) { int num2 = patch.ReadyCount(num); double num3 = patch.NextReadySec(num); string text = ((num3 > 0.0) ? $" next in {(num3 - num) / 60.0:0} min" : ""); Say(ctx, $" #{patch.Id} {patch.Kind} {patch.Item} ({patch.Center.x:0},{patch.Center.z:0}) x{patch.Count} {num2} ready{text}"); } } private static void Catalog(Terminal ctx) { ResourceCatalog.EnsureBuilt(); if (!ResourceCatalog.Built) { Say(ctx, "Trove: no world loaded, nothing to classify yet."); return; } int num = 0; foreach (ResourceCatalog.Info item in ResourceCatalog.All) { num++; Say(ctx, string.Format(" {0,-9} {1,-32} -> {2,-20} {3}{4}{5}", item.Kind, item.Prefab, item.Item, item.Via, (item.RespawnMinutes > 0f) ? (" respawn " + item.RespawnMinutes + " min") : "", ((Object)(object)item.Icon == (Object)null) ? " (no icon)" : "")); if (item.Drops.Length > 0) { Say(ctx, " drops: " + item.Drops); } } Say(ctx, "Trove: " + num + " prefab(s) classified."); } } public static class PatchCache { private const string Header = "# Trove cache v1: patch\tkind\titem\tprefab\tx\ty\tz\trespawnMin\tpicked\tpickedSec\tflags (kind X = forgotten spot, only x y z used)"; private static string _path; private static long _loadedWorld; public static bool Loaded => _path != null; public static string Path => _path; public static void EnsureLoaded() { World world = ZNet.World; if (world != null) { long num = ((world.m_uid != 0L) ? world.m_uid : world.m_seed); if (_path == null || _loadedWorld != num) { Unload(); _loadedWorld = num; _path = PathFor(world, num); Load(); } } } public static void Unload() { if (PatchStore.Dirty) { Save(); } PatchStore.Clear(); _path = null; _loadedWorld = 0L; } public static void SaveIfDirty() { if (PatchStore.Dirty) { Save(); } } private static string PathFor(World world, long id) { StringBuilder stringBuilder = new StringBuilder(); string text = world.m_name ?? "world"; foreach (char c in text) { stringBuilder.Append((char.IsLetterOrDigit(c) || c == '-' || c == '_') ? c : '_'); } if (stringBuilder.Length == 0) { stringBuilder.Append("world"); } return System.IO.Path.Combine(System.IO.Path.Combine(Paths.ConfigPath, "Trove"), stringBuilder?.ToString() + "-" + id.ToString(CultureInfo.InvariantCulture) + ".tsv"); } private static void Load() { //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) PatchStore.Clear(); if (!File.Exists(_path)) { TrovePlugin.Log.LogInfo((object)("no cache yet at " + _path)); return; } int num = 0; try { string[] array = File.ReadAllLines(_path); foreach (string text in array) { if (text.Length == 0 || text[0] == '#') { continue; } string[] array2 = text.Split(new char[1] { '\t' }); if (array2.Length < 10) { num++; continue; } ResourceKind kind; switch (array2[1]) { case "P": kind = ResourceKind.Pickable; break; case "S": kind = ResourceKind.OneShot; break; case "O": kind = ResourceKind.Ore; break; case "X": PatchStore.RestoreForgotten(Member.MakeKey(new Vector3(F(array2[4]), F(array2[5]), F(array2[6])))); continue; default: num++; continue; } string text2 = ((array2.Length > 10) ? array2[10] : ""); PatchStore.Restore(int.Parse(array2[0], CultureInfo.InvariantCulture), kind, array2[2], array2[3], new Vector3(F(array2[4]), F(array2[5]), F(array2[6])), F(array2[7]), array2[8] == "1", double.Parse(array2[9], CultureInfo.InvariantCulture), text2.IndexOf('c') >= 0); } } catch (Exception ex) { TrovePlugin.Log.LogWarning((object)("could not read cache " + _path + ": " + ex.Message)); PatchStore.Clear(); return; } PatchStore.MarkSaved(); TrovePlugin.Log.LogInfo((object)string.Format("loaded {0} patch(es), {1} member(s) from {2}{3}", PatchStore.Patches.Count, PatchStore.MemberCount, System.IO.Path.GetFileName(_path), (num > 0) ? (", skipped " + num + " bad line(s)") : "")); } private static void Save() { if (_path == null) { return; } try { Directory.CreateDirectory(System.IO.Path.GetDirectoryName(_path)); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("# Trove cache v1: patch\tkind\titem\tprefab\tx\ty\tz\trespawnMin\tpicked\tpickedSec\tflags (kind X = forgotten spot, only x y z used)").Append('\n'); foreach (Patch patch in PatchStore.Patches) { string value = ((patch.Kind == ResourceKind.Ore) ? "O" : ((patch.Kind == ResourceKind.OneShot) ? "S" : "P")); for (int i = 0; i < patch.Members.Count; i++) { Member member = patch.Members[i]; stringBuilder.Append(patch.Id.ToString(CultureInfo.InvariantCulture)).Append('\t').Append(value) .Append('\t') .Append(patch.Item) .Append('\t') .Append(member.Prefab) .Append('\t') .Append(S(member.Pos.x)) .Append('\t') .Append(S(member.Pos.y)) .Append('\t') .Append(S(member.Pos.z)) .Append('\t') .Append(S(member.RespawnMinutes)) .Append('\t') .Append(member.Picked ? '1' : '0') .Append('\t') .Append(member.PickedSec.ToString("0.#", CultureInfo.InvariantCulture)) .Append('\t') .Append(patch.ManualChecked ? "c" : "") .Append('\n'); } } foreach (string item in PatchStore.Forgotten) { string[] array = item.Split(new char[1] { '|' }); if (array.Length == 3) { stringBuilder.Append("0\tX\t\t\t").Append(array[0]).Append('\t') .Append(array[1]) .Append('\t') .Append(array[2]) .Append("\t0\t0\t0\t\n"); } } string text = _path + ".tmp"; File.WriteAllText(text, stringBuilder.ToString()); if (File.Exists(_path)) { File.Delete(_path); } File.Move(text, _path); PatchStore.MarkSaved(); if (PluginConfig.Verbose.Value) { TrovePlugin.Log.LogDebug((object)("saved " + PatchStore.MemberCount + " member(s) to " + System.IO.Path.GetFileName(_path))); } } catch (Exception ex) { TrovePlugin.Log.LogWarning((object)("could not write cache " + _path + ": " + ex.Message)); } } private static string S(float v) { return v.ToString("0.##", CultureInfo.InvariantCulture); } private static float F(string s) { return float.Parse(s, CultureInfo.InvariantCulture); } } public static class PatchStore { private static readonly List<Patch> _patches = new List<Patch>(); private static readonly Dictionary<string, Member> _members = new Dictionary<string, Member>(); private static readonly Dictionary<Member, Patch> _owner = new Dictionary<Member, Patch>(); private static readonly HashSet<string> _forgotten = new HashSet<string>(); private static int _nextId = 1; public static int Version { get; private set; } public static bool Dirty { get; private set; } public static IReadOnlyList<Patch> Patches => _patches; public static int MemberCount => _members.Count; public static IEnumerable<string> Forgotten => _forgotten; public static int ForgottenCount => _forgotten.Count; public static void Clear() { _patches.Clear(); _members.Clear(); _owner.Clear(); _forgotten.Clear(); _nextId = 1; Dirty = false; Version++; } public static void RestoreForgotten(string key) { _forgotten.Add(key); } public static void Unforget() { if (_forgotten.Count != 0) { _forgotten.Clear(); Touch(); } } public static void ForgetAll() { Clear(); Dirty = true; } public static void MarkSaved() { Dirty = false; } public static double Now() { ZNet instance = ZNet.instance; if (!((Object)(object)instance != (Object)null)) { return 0.0; } return instance.GetTimeSeconds(); } public static Member Find(Vector3 pos) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) if (!_members.TryGetValue(Member.MakeKey(pos), out var value)) { return null; } return value; } public static Patch PatchOf(Member m) { if (!_owner.TryGetValue(m, out var value)) { return null; } return value; } public static Patch Record(ResourceKind kind, string item, string prefab, Vector3 pos, float respawnMinutes, bool picked, double now, float clusterRadius, out bool created) { //IL_0004: 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_00dc: 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_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) created = false; string text = Member.MakeKey(pos); if (_forgotten.Contains(text)) { return null; } Patch patch; if (_members.TryGetValue(text, out var value)) { patch = PatchOf(value); if (patch != null && patch.Item == item) { bool flag = false; if (picked && !value.Picked) { value.Picked = true; value.PickedSec = now; flag = true; } if (value.RespawnMinutes != respawnMinutes) { value.RespawnMinutes = respawnMinutes; flag = true; } if (flag) { Touch(); } return patch; } RemoveMember(value); } patch = Nearest(kind, item, pos, clusterRadius); if (patch == null) { patch = new Patch { Id = _nextId++, Kind = kind, Item = item, Center = pos }; _patches.Add(patch); created = true; } value = new Member { Key = text, Pos = pos, Prefab = prefab, RespawnMinutes = respawnMinutes, Picked = picked, PickedSec = (picked ? now : 0.0) }; _members[text] = value; _owner[value] = patch; patch.Members.Add(value); patch.Recenter(); Touch(); return patch; } public static void Restore(int patchId, ResourceKind kind, string item, string prefab, Vector3 pos, float respawnMinutes, bool picked, double pickedSec, bool manualChecked) { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0058: 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_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) Patch patch = null; for (int i = 0; i < _patches.Count; i++) { if (_patches[i].Id == patchId) { patch = _patches[i]; break; } } if (patch == null) { patch = new Patch { Id = patchId, Kind = kind, Item = item, Center = pos }; _patches.Add(patch); if (patchId >= _nextId) { _nextId = patchId + 1; } } if (manualChecked) { patch.ManualChecked = true; } string key = Member.MakeKey(pos); if (!_members.ContainsKey(key)) { Member member = new Member { Key = key, Pos = pos, Prefab = prefab, RespawnMinutes = respawnMinutes, Picked = picked, PickedSec = pickedSec }; _members[key] = member; _owner[member] = patch; patch.Members.Add(member); patch.Recenter(); Version++; } } public static void Observe(Vector3 pos, bool picked, double pickedSecOrZero) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) Member member = Find(pos); if (member == null) { return; } bool flag = false; if (member.Picked != picked) { member.Picked = picked; flag = true; if (!picked) { Patch patch = PatchOf(member); if (patch != null) { patch.ManualChecked = false; } } } if (picked && pickedSecOrZero > 0.0 && Math.Abs(member.PickedSec - pickedSecOrZero) > 1.0) { member.PickedSec = pickedSecOrZero; flag = true; } else if (picked && member.PickedSec <= 0.0) { member.PickedSec = Now(); flag = true; } if (flag) { Touch(); } } public static bool NoteDestroyed(Vector3 pos) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) Member member = Find(pos); if (member == null) { return false; } RemoveMember(member); Touch(); return true; } public static void Forget(Patch patch) { for (int num = patch.Members.Count - 1; num >= 0; num--) { Member member = patch.Members[num]; _members.Remove(member.Key); _owner.Remove(member); _forgotten.Add(member.Key); } patch.Members.Clear(); _patches.Remove(patch); Touch(); } public static void ToggleManualCheck(Patch patch) { patch.ManualChecked = !patch.ManualChecked; Touch(); } public static Patch NearestAny(Vector3 pos, float radius) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) Patch result = null; float num = radius; for (int i = 0; i < _patches.Count; i++) { float num2 = Utils.DistanceXZ(_patches[i].Center, pos); if (num2 <= num) { num = num2; result = _patches[i]; } } return result; } private static void RemoveMember(Member m) { _members.Remove(m.Key); if (_owner.TryGetValue(m, out var value)) { _owner.Remove(m); value.Members.Remove(m); if (value.Members.Count == 0) { _patches.Remove(value); } else { value.Recenter(); } } } private static Patch Nearest(ResourceKind kind, string item, Vector3 pos, float radius) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) Patch result = null; float num = radius; for (int i = 0; i < _patches.Count; i++) { Patch patch = _patches[i]; if (patch.Kind == kind && !(patch.Item != item)) { float num2 = Utils.DistanceXZ(patch.Center, pos); if (num2 <= num) { num = num2; result = patch; } } } return result; } private static void Touch() { Dirty = true; Version++; } } public static class ResourceCatalog { public sealed class Info { public string Prefab = ""; public ResourceKind Kind; public string Item = ""; public string ItemToken = ""; public float RespawnMinutes; public Sprite Icon; public string Via = ""; public string Drops = ""; } private static readonly Dictionary<string, Info> _byPrefab = new Dictionary<string, Info>(); private static readonly Dictionary<string, Info> _byItem = new Dictionary<string, Info>(); private static HashSet<string> _ignoredDrops = new HashSet<string>(); private static string[] _excludedPrefabs = new string[0]; private static bool _built; public static bool Built => _built; public static IEnumerable<Info> All => _byPrefab.Values; public static void Clear() { _byPrefab.Clear(); _byItem.Clear(); _built = false; } public static Info Get(string prefab) { if (prefab == null || !_byPrefab.TryGetValue(prefab, out var value)) { return null; } return value; } public static Info ForItem(string item) { if (item == null || !_byItem.TryGetValue(item, out var value)) { return null; } return value; } public static void EnsureBuilt() { if (_built) { return; } ZNetScene instance = ZNetScene.instance; if ((Object)(object)instance == (Object)null || instance.m_prefabs == null || instance.m_prefabs.Count == 0) { return; } _ignoredDrops = Split(PluginConfig.IgnoredDrops.Value); _excludedPrefabs = new List<string>(Split(PluginConfig.ExcludedPrefabs.Value)).ToArray(); int num = 0; int num2 = 0; int num3 = 0; foreach (GameObject prefab in instance.m_prefabs) { if ((Object)(object)prefab == (Object)null) { continue; } Info info = Classify(prefab); if (info != null) { _byPrefab[info.Prefab] = info; if (!_byItem.ContainsKey(info.Item)) { _byItem[info.Item] = info; } switch (info.Kind) { case ResourceKind.Pickable: num++; break; case ResourceKind.OneShot: num2++; break; case ResourceKind.Ore: num3++; break; } } } _built = true; TrovePlugin.Log.LogInfo((object)$"catalogue: {num} respawning pickable(s), {num2} one-shot pickable(s), {num3} ore node prefab(s); 'trove catalog' lists them"); } private static Info Classify(GameObject prefab) { //IL_0181: 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_01b4: Unknown result type (might be due to invalid IL or missing references) Pickable component = prefab.GetComponent<Pickable>(); if ((Object)(object)component != (Object)null) { if ((Object)(object)component.m_itemPrefab == (Object)null) { return null; } Info obj = new Info { Prefab = ((Object)prefab).name, Kind = ((component.m_respawnTimeMinutes > 0f) ? ResourceKind.Pickable : ResourceKind.OneShot), Item = ((Object)component.m_itemPrefab).name, RespawnMinutes = component.m_respawnTimeMinutes, Via = "Pickable" }; FillItem(obj, component.m_itemPrefab); return obj; } DropTable val = null; string via = null; MineRock5 component2 = prefab.GetComponent<MineRock5>(); if ((Object)(object)component2 != (Object)null) { val = component2.m_dropItems; via = "MineRock5"; } else { MineRock component3 = prefab.GetComponent<MineRock>(); if ((Object)(object)component3 != (Object)null) { val = component3.m_dropItems; via = "MineRock"; } else { Destructible component4 = prefab.GetComponent<Destructible>(); if ((Object)(object)component4 != (Object)null) { MineRock5 val2 = (((Object)(object)component4.m_spawnWhenDestroyed != (Object)null) ? component4.m_spawnWhenDestroyed.GetComponent<MineRock5>() : null); if ((Object)(object)val2 != (Object)null) { val = val2.m_dropItems; via = "Destructible>" + ((Object)component4.m_spawnWhenDestroyed).name; } else { DropOnDestroyed component5 = prefab.GetComponent<DropOnDestroyed>(); if ((Object)(object)component5 != (Object)null && IsPickaxeOnly(component4.m_damages)) { val = component5.m_dropWhenDestroyed; via = "Destructible+DropOnDestroyed"; } } } } } if (val == null || val.m_drops == null) { return null; } GameObject val3 = null; float num = float.MinValue; StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < val.m_drops.Count; i++) { GameObject item = val.m_drops[i].m_item; if (!((Object)(object)item == (Object)null) && !((Object)(object)item.GetComponent<ItemDrop>() == (Object)null)) { float weight = val.m_drops[i].m_weight; if (stringBuilder.Length > 0) { stringBuilder.Append(", "); } stringBuilder.Append(((Object)item).name).Append(' ').Append(weight.ToString("0.##", CultureInfo.InvariantCulture)); if (_ignoredDrops.Contains(((Object)item).name)) { stringBuilder.Append("(ignored)"); } else if ((Object)(object)val3 == (Object)null || weight > num) { val3 = item; num = weight; } } } if ((Object)(object)val3 == (Object)null) { return null; } if (IsExcludedPrefab(((Object)prefab).name)) { return null; } Info obj2 = new Info { Prefab = ((Object)prefab).name, Kind = ResourceKind.Ore, Item = ((Object)val3).name, Via = via, Drops = stringBuilder.ToString() }; FillItem(obj2, val3); return obj2; } private static bool IsExcludedPrefab(string name) { for (int i = 0; i < _excludedPrefabs.Length; i++) { string text = _excludedPrefabs[i]; if (text.Length == 0) { continue; } if (text[text.Length - 1] == '*') { if (name.StartsWith(text.Substring(0, text.Length - 1), StringComparison.OrdinalIgnoreCase)) { return true; } } else if (string.Equals(name, text, StringComparison.OrdinalIgnoreCase)) { return true; } } return false; } private static bool IsPickaxeOnly(DamageModifiers m) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Invalid comparison between Unknown and I4 //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Invalid comparison between Unknown and I4 //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Invalid comparison between Unknown and I4 if ((int)m.m_pickaxe != 3 && (int)m.m_chop == 3 && (int)m.m_blunt == 3) { return (int)m.m_slash == 3; } return false; } private static void FillItem(Info info, GameObject itemPrefab) { ItemDrop component = itemPrefab.GetComponent<ItemDrop>(); if ((Object)(object)component == (Object)null || component.m_itemData == null || component.m_itemData.m_shared == null) { info.ItemToken = info.Item; return; } info.ItemToken = component.m_itemData.m_shared.m_name ?? info.Item; try { Sprite[] icons = component.m_itemData.m_shared.m_icons; if (icons != null && icons.Length != 0) { info.Icon = component.m_itemData.GetIcon(); } } catch (Exception ex) { if (PluginConfig.Verbose.Value) { TrovePlugin.Log.LogDebug((object)("no icon for " + info.Item + ": " + ex.Message)); } } } public static HashSet<string> Split(string csv) { HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase); if (string.IsNullOrEmpty(csv)) { return hashSet; } string[] array = csv.Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.Length > 0) { hashSet.Add(text); } } return hashSet; } public static string DisplayName(Info info) { if (info == null) { return ""; } string itemToken = info.ItemToken; if (string.IsNullOrEmpty(itemToken)) { return info.Item; } Localization instance = Localization.instance; string text = ((instance != null) ? instance.Localize(itemToken) : itemToken); if (string.IsNullOrEmpty(text) || text.StartsWith("[", StringComparison.Ordinal)) { return info.Item; } return text; } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }