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 CraftFromContainerGroups v0.10.1
CraftFromContainerGroups.dll
Decompiled 4 days ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")] [assembly: AssemblyCompany("CraftFromContainerGroups")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.10.1.0")] [assembly: AssemblyInformationalVersion("0.10.1+046c5e6621ebb4fc892eefcc8ca492c1ed0fc0e3")] [assembly: AssemblyProduct("CraftFromContainerGroups")] [assembly: AssemblyTitle("CraftFromContainerGroups")] [assembly: AssemblyVersion("0.10.1.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace CraftFromContainerGroups { internal static class Audit { private sealed class Snapshot { public Dictionary<string, int> Counts; public long Owner; } private sealed class MachineState { public Dictionary<string, int> Queue; public float Fuel; public long Owner; } private sealed class CookState { public string[] Slots; public long Owner; } private static readonly Dictionary<Container, Snapshot> Last = new Dictionary<Container, Snapshot>(); private static readonly Dictionary<ZDOID, MachineState> MachineStates = new Dictionary<ZDOID, MachineState>(); private static float _nextMachineCheck; private static readonly Dictionary<ZDOID, CookState> CookStates = new Dictionary<ZDOID, CookState>(); private static readonly FieldInfo FunctionsField = AccessTools.Field(typeof(ZNetView), "m_functions"); private static readonly Dictionary<string, float> LastUnknown = new Dictionary<string, float>(); public static void Apply(Harmony h) { Util.Patch(h, "Container.Load (audit)", AccessTools.Method(typeof(Container), "Load", (Type[])null, (Type[])null), typeof(Audit), null, "AfterLoad"); Util.Patch(h, "Container.Save (audit)", AccessTools.Method(typeof(Container), "Save", (Type[])null, (Type[])null), typeof(Audit), null, "AfterSave"); Util.Patch(h, "ZNetView.HandleRoutedRPC (audit)", AccessTools.Method(typeof(ZNetView), "HandleRoutedRPC", (Type[])null, (Type[])null), typeof(Audit), "BeforeRpc"); Util.Patch(h, "Smelter.RPC_AddOre (audit)", AccessTools.Method(typeof(Smelter), "RPC_AddOre", (Type[])null, (Type[])null), typeof(Audit), "AuditAddOre"); Util.Patch(h, "Smelter.QueueProcessed (audit)", AccessTools.Method(typeof(Smelter), "QueueProcessed", (Type[])null, (Type[])null), typeof(Audit), "AuditMade"); } private static string At(Component c) { return Util.At(c); } private static string GameOf(long uid) { if (uid != ZNet.GetUID()) { return Chests.PlayerName(uid) + "'s game"; } return "your game"; } private static bool Watched(Container c, ZNetView nview) { if (Groups.Of(nview).Count <= 0) { return Groups.BuiltByMe((Component)(object)c); } return true; } private static string HowFar(long who, Vector3 pos) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) try { foreach (Player allPlayer in Player.GetAllPlayers()) { ZNetView val = (((Object)(object)allPlayer != (Object)null) ? ((Component)allPlayer).GetComponent<ZNetView>() : null); if (!((Object)(object)val == (Object)null) && val.IsValid() && val.GetZDO().GetOwner() == who) { float num = Vector3.Distance(((Component)allPlayer).transform.position, pos); return (num <= 5f) ? "standing at it" : $"{Mathf.RoundToInt(num)} m away, too far to reach by hand"; } } } catch { } return "not near you, too far to reach by hand"; } private static Dictionary<string, int> Count(Container c) { Dictionary<string, int> dictionary = new Dictionary<string, int>(); Inventory inventory = c.GetInventory(); if (inventory == null) { return dictionary; } foreach (ItemData allItem in inventory.GetAllItems()) { string name = allItem.m_shared.m_name; dictionary[name] = (dictionary.TryGetValue(name, out var value) ? value : 0) + allItem.m_stack; } return dictionary; } private static void AfterSave(Container __instance) { try { if (Plugin.AuditLog.Value && !((Object)(object)__instance == (Object)null)) { ZNetView val = Util.NView((Component)(object)__instance); if (!((Object)(object)val == (Object)null) && val.IsValid() && Watched(__instance, val)) { Last[__instance] = new Snapshot { Counts = Count(__instance), Owner = val.GetZDO().GetOwner() }; } } } catch { } } private static void AfterLoad(Container __instance, bool __result) { //IL_0291: Unknown result type (might be due to invalid IL or missing references) try { if (!__result || (Object)(object)__instance == (Object)null) { return; } ZNetView val = Util.NView((Component)(object)__instance); if ((Object)(object)val == (Object)null || !val.IsValid()) { return; } if (Groups.Of(val).Count > 0) { Chests.InvalidateCache(); } if (!Plugin.AuditLog.Value) { return; } if (!Watched(__instance, val)) { Last.Remove(__instance); return; } long owner = val.GetZDO().GetOwner(); Dictionary<string, int> dictionary = Count(__instance); Last.TryGetValue(__instance, out var value); Last[__instance] = new Snapshot { Counts = dictionary, Owner = owner }; if (Last.Count > 4096) { Last.Clear(); } else { if (value == null) { return; } long uID = ZNet.GetUID(); long num = ((owner != uID) ? owner : value.Owner); if (num == uID || num == 0L) { return; } List<string> list = new List<string>(); foreach (KeyValuePair<string, int> count in value.Counts) { int value2; int num2 = (dictionary.TryGetValue(count.Key, out value2) ? value2 : 0); if (num2 < count.Value) { list.Add($"-{count.Value - num2} {Transfer.Name(count.Key)}"); } } foreach (KeyValuePair<string, int> item in dictionary) { int value3; int num3 = (value.Counts.TryGetValue(item.Key, out value3) ? value3 : 0); if (item.Value > num3) { list.Add($"+{item.Value - num3} {Transfer.Name(item.Key)}"); } } if (list.Count != 0) { Plugin.Log.LogInfo((object)("[Audit] " + At((Component)(object)__instance) + " [" + Groups.Display(val) + "]: " + string.Join(", ", list.ToArray()) + " (by " + GameOf(num) + "; " + Chests.PlayerName(num) + " was " + HowFar(num, ((Component)__instance).transform.position) + ")")); } } } catch { } } public static void Tick() { //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_033d: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.AuditLog.Value || Time.time < _nextMachineCheck || (Object)(object)Player.m_localPlayer == (Object)null) { return; } _nextMachineCheck = Time.time + 2f; long uID = ZNet.GetUID(); foreach (MonoBehaviour smelter in Registry.Smelters) { try { ZNetView val = (((Object)(object)smelter != (Object)null) ? Util.NView((Component)(object)smelter) : null); if ((Object)(object)val == (Object)null || !val.IsValid()) { continue; } ZDO zDO = val.GetZDO(); Dictionary<string, int> dictionary = new Dictionary<string, int>(); int num = zDO.GetInt(ZDOVars.s_queued, 0); for (int i = 0; i < num && i < 200; i++) { string text = zDO.GetString("item" + i, ""); if (text.Length > 0) { dictionary[text] = (dictionary.TryGetValue(text, out var value) ? value : 0) + 1; } } MachineState machineState = new MachineState { Queue = dictionary, Fuel = zDO.GetFloat(ZDOVars.s_fuel, 0f), Owner = zDO.GetOwner() }; MachineStates.TryGetValue(zDO.m_uid, out var value2); MachineStates[zDO.m_uid] = machineState; if ((value2 == null || value2.Owner != machineState.Owner) && machineState.Owner != uID && machineState.Owner != 0L) { Plugin.Log.LogInfo((object)("[Audit] " + At((Component)(object)smelter) + " is now controlled by " + Chests.PlayerName(machineState.Owner))); } if (value2 == null) { continue; } long owner = machineState.Owner; if (owner == uID || owner == 0L) { continue; } List<string> list = new List<string>(); List<string> list2 = new List<string>(); Smelter val2 = (Smelter)(object)((smelter is Smelter) ? smelter : null); foreach (KeyValuePair<string, int> item in machineState.Queue) { int value3; int num2 = (value2.Queue.TryGetValue(item.Key, out value3) ? value3 : 0); if (item.Value > num2) { list.Add($"+{item.Value - num2} {item.Key} queued"); if ((Object)(object)val2 != (Object)null && !Accepts(val2, item.Key)) { list2.Add(item.Key); } } } float num3 = machineState.Fuel - value2.Fuel; if (num3 >= 0.99f) { list.Add($"+{Mathf.RoundToInt(num3)} fuel"); } if (list.Count != 0) { string text2 = "[Audit] " + At((Component)(object)smelter) + ": " + string.Join(", ", list.ToArray()) + " (by " + GameOf(owner) + "; " + Chests.PlayerName(owner) + " was " + HowFar(owner, ((Component)smelter).transform.position) + ")"; if (list2.Count > 0) { text2 = text2 + " WARNING: a normal " + ((Object)smelter).name.Replace("(Clone)", "") + " doesn't accept " + string.Join(", ", list2.ToArray()) + ": that game runs a mod that changes what this machine takes"; } Plugin.Log.LogInfo((object)text2); } } catch { } } if (MachineStates.Count > 2048) { MachineStates.Clear(); } foreach (MonoBehaviour item2 in Registry.Cooking) { try { CheckCooking((CookingStation)(object)((item2 is CookingStation) ? item2 : null), uID); } catch { } } if (CookStates.Count > 2048) { CookStates.Clear(); } } private static void CheckCooking(CookingStation c, long me) { //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_031d: Unknown result type (might be due to invalid IL or missing references) ZNetView val = (((Object)(object)c != (Object)null) ? Util.NView((Component)(object)c) : null); if ((Object)(object)val == (Object)null || !val.IsValid() || c.m_slots == null) { return; } ZDO zDO = val.GetZDO(); CookState cookState = new CookState { Slots = new string[c.m_slots.Length], Owner = zDO.GetOwner() }; for (int i = 0; i < cookState.Slots.Length; i++) { cookState.Slots[i] = zDO.GetString("slot" + i, "") ?? ""; } CookStates.TryGetValue(zDO.m_uid, out var value); CookStates[zDO.m_uid] = cookState; if ((value == null || value.Owner != cookState.Owner) && cookState.Owner != me && cookState.Owner != 0L) { Plugin.Log.LogInfo((object)("[Audit] " + At((Component)(object)c) + " is now controlled by " + Chests.PlayerName(cookState.Owner))); } if (value == null || value.Slots.Length != cookState.Slots.Length) { return; } long num = ((cookState.Owner != me) ? cookState.Owner : value.Owner); if (num == me || num == 0L) { return; } Dictionary<string, int> dictionary = new Dictionary<string, int>(); Dictionary<string, int> dictionary2 = new Dictionary<string, int>(); for (int j = 0; j < cookState.Slots.Length; j++) { string text = value.Slots[j]; string text2 = cookState.Slots[j]; if (text.Length > 0 && text2.Length == 0) { dictionary[text] = (dictionary.TryGetValue(text, out var value2) ? value2 : 0) + 1; } else if (text.Length == 0 && text2.Length > 0) { dictionary2[text2] = (dictionary2.TryGetValue(text2, out var value3) ? value3 : 0) + 1; } } if (dictionary.Count == 0 && dictionary2.Count == 0) { return; } List<string> list = new List<string>(); foreach (KeyValuePair<string, int> item in dictionary) { list.Add($"{item.Value} {ItemName(item.Key)} taken off"); } foreach (KeyValuePair<string, int> item2 in dictionary2) { list.Add($"{item2.Value} {ItemName(item2.Key)} put on"); } Plugin.Log.LogInfo((object)("[Audit] " + At((Component)(object)c) + ": " + string.Join(", ", list.ToArray()) + " (by " + GameOf(num) + "; " + Chests.PlayerName(num) + " was " + HowFar(num, ((Component)c).transform.position) + ")")); } private static string ItemName(string prefab) { try { object obj; if (!((Object)(object)ObjectDB.instance != (Object)null)) { obj = null; } else { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(prefab); obj = ((itemPrefab != null) ? itemPrefab.GetComponent<ItemDrop>() : null); } ItemDrop val = (ItemDrop)obj; if ((Object)(object)val != (Object)null) { return Transfer.Name(val.m_itemData.m_shared.m_name); } } catch { } return prefab; } private static bool Accepts(Smelter s, string prefabName) { foreach (ItemConversion item in s.m_conversion) { if (item != null && (Object)(object)item.m_from != (Object)null && ((Object)((Component)item.m_from).gameObject).name == prefabName) { return true; } } return false; } private static void AuditAddOre(Smelter __instance, long sender, string name) { try { if (Plugin.AuditLog.Value) { ZNetView val = Util.NView((Component)(object)__instance); if (!((Object)(object)val == (Object)null) && val.IsValid() && val.IsOwner()) { Plugin.Log.LogInfo((object)("[Audit] " + At((Component)(object)__instance) + ": " + name + " added by " + Chests.PlayerName(sender))); } } } catch { } } private static void AuditMade(Smelter __instance, string ore) { try { if (Plugin.AuditLog.Value) { Plugin.Log.LogInfo((object)("[Audit] " + At((Component)(object)__instance) + ": finished one " + ore)); } } catch { } } private static void BeforeRpc(ZNetView __instance, RoutedRPCData rpcData) { try { if (!Plugin.AuditLog.Value || FunctionsField == null || (Object)(object)__instance == (Object)null || rpcData == null || !(FunctionsField.GetValue(__instance) is IDictionary dictionary) || dictionary.Contains(rpcData.m_methodHash)) { return; } string key = rpcData.m_senderPeerID + "|" + rpcData.m_methodHash + "|" + ((Object)__instance).name; if (!LastUnknown.TryGetValue(key, out var value) || !(Time.time - value < 30f)) { LastUnknown[key] = Time.time; if (LastUnknown.Count > 512) { LastUnknown.Clear(); } Plugin.Log.LogInfo((object)($"[Audit] Unknown message {rpcData.m_methodHash} to {At((Component)(object)__instance)} " + "from " + GameOf(rpcData.m_senderPeerID) + " (a mod on that game sent it)")); } } catch { } } } internal static class Chests { private sealed class CacheEntry { public List<Container> Chests; public float Time; public readonly Dictionary<string, int> Counts = new Dictionary<string, int>(); } private static readonly HashSet<Container> All = new HashSet<Container>(); private static readonly Func<Container, long, bool> CheckAccessFn = Util.Link<Func<Container, long, bool>>(AccessTools.Method(typeof(Container), "CheckAccess", (Type[])null, (Type[])null)); private const float DisplayCacheSeconds = 0.5f; private static readonly Dictionary<string, CacheEntry> DisplayCache = new Dictionary<string, CacheEntry>(); private static HashSet<string> _lastGroupsRef; private static string _lastGroupsSig; private static CacheEntry _lastEntry; private static HashSet<string> _lastEntryGroups; private static int _lx; private static int _ly; private static int _lz; public static int Version { get; private set; } public static void Register(Container c) { if ((Object)(object)c != (Object)null) { All.Add(c); } } public static IEnumerable<Container> AllLoaded() { All.RemoveWhere((Container c) => (Object)(object)c == (Object)null); return All; } public static void InvalidateCache() { DisplayCache.Clear(); _lastEntry = null; Version++; } public static List<Container> Eligible(Vector3 origin, HashSet<string> groups) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return Entry(origin, groups).Chests; } private static CacheEntry Entry(Vector3 origin, HashSet<string> groups) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003f: 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) if (groups == null || groups.Count == 0) { return new CacheEntry { Chests = new List<Container>(), Time = Time.time }; } int num = Mathf.RoundToInt(origin.x); int num2 = Mathf.RoundToInt(origin.y); int num3 = Mathf.RoundToInt(origin.z); if (_lastEntry != null && groups == _lastEntryGroups && num == _lx && num2 == _ly && num3 == _lz && Time.time - _lastEntry.Time < 0.5f) { return _lastEntry; } CacheEntry result = (_lastEntry = EntrySlow(groups, num, num2, num3, origin)); _lastEntryGroups = groups; _lx = num; _ly = num2; _lz = num3; return result; } private static CacheEntry EntrySlow(HashSet<string> groups, int x, int y, int z, Vector3 origin) { //IL_00c1: Unknown result type (might be due to invalid IL or missing references) if (groups != _lastGroupsRef) { List<string> list = new List<string>(groups); list.Sort(StringComparer.OrdinalIgnoreCase); _lastGroupsSig = string.Join(",", list.ToArray()); _lastGroupsRef = groups; } string key = _lastGroupsSig + "|" + x + "," + y + "," + z; if (DisplayCache.TryGetValue(key, out var value) && Time.time - value.Time < 0.5f) { return value; } if (DisplayCache.Count > 32) { DisplayCache.Clear(); } CacheEntry cacheEntry = new CacheEntry { Chests = Fresh(origin, groups), Time = Time.time }; DisplayCache[key] = cacheEntry; return cacheEntry; } public static List<Container> Fresh(Vector3 origin, HashSet<string> groups) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) List<Container> list = new List<Container>(); if (groups == null || groups.Count == 0) { return list; } All.RemoveWhere((Container c) => (Object)(object)c == (Object)null); float value = Plugin.Range.Value; long playerId = (((Object)(object)Player.m_localPlayer != (Object)null) ? Player.m_localPlayer.GetPlayerID() : 0); foreach (Container item in All) { if (InGroupsAndRange(item, origin, groups, value) && !item.IsInUse() && HasAccess(item, playerId) && (!item.m_checkGuardStone || PrivateArea.CheckAccess(((Component)item).transform.position, 0f, false, false))) { list.Add(item); } } list.Sort(delegate(Container a, Container b) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002d: 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_0037: Unknown result type (might be due to invalid IL or missing references) Vector3 val = origin - ((Component)a).transform.position; float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude; val = origin - ((Component)b).transform.position; return sqrMagnitude.CompareTo(((Vector3)(ref val)).sqrMagnitude); }); return list; } public static List<Container> Busy(Vector3 origin, HashSet<string> groups) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) List<Container> list = new List<Container>(); if (groups == null || groups.Count == 0) { return list; } float value = Plugin.Range.Value; foreach (Container item in All) { if ((Object)(object)item != (Object)null && InGroupsAndRange(item, origin, groups, value) && item.IsInUse()) { list.Add(item); } } return list; } private static bool InGroupsAndRange(Container c, Vector3 origin, HashSet<string> groups, float range) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //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_001c: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)c == (Object)null) { return false; } Vector3 val = origin - ((Component)c).transform.position; if (((Vector3)(ref val)).sqrMagnitude > range * range) { return false; } ZNetView val2 = Util.NView((Component)(object)c); if ((Object)(object)val2 == (Object)null || !val2.IsValid()) { return false; } if (!Groups.Of(val2).Overlaps(groups)) { return false; } return c.GetInventory() != null; } public static string UserOf(Container c) { try { ZNetView val = Util.NView((Component)(object)c); if ((Object)(object)val == (Object)null || !val.IsValid() || (Object)(object)ZNet.instance == (Object)null) { return "Someone"; } return PlayerName(val.GetZDO().GetOwner()); } catch { } return "Someone"; } public static string PlayerName(long uid) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0045: 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) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) try { if (uid == 0L) { return "nobody"; } if (uid == ZNet.GetUID()) { return "You"; } if ((Object)(object)ZNet.instance != (Object)null) { foreach (PlayerInfo player in ZNet.instance.GetPlayerList()) { ZDOID characterID = player.m_characterID; if (((ZDOID)(ref characterID)).UserID == uid && !string.IsNullOrEmpty(player.m_name)) { return player.m_name; } } } } catch { } return "Someone"; } private static bool HasAccess(Container c, long playerId) { if (CheckAccessFn == null) { return true; } try { return CheckAccessFn(c, playerId); } catch { return true; } } public static bool KeepsOne(Container c) { return Groups.GetInt(Util.NView((Component)(object)c), "cfcg_keep1") != 0; } public static int Spare(Container c, string itemName) { if ((Object)(object)c == (Object)null || c.GetInventory() == null) { return 0; } int num = c.GetInventory().CountItems(itemName, -1, true); if (num <= 0 || !KeepsOne(c)) { return num; } return num - 1; } public static int Count(Vector3 origin, HashSet<string> groups, string itemName) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) CacheEntry cacheEntry = Entry(origin, groups); if (cacheEntry.Counts.TryGetValue(itemName, out var value)) { return value; } int num = 0; foreach (Container chest in cacheEntry.Chests) { num += Spare(chest, itemName); } cacheEntry.Counts[itemName] = num; return num; } } internal static class Compat { private static readonly FieldInfo SelectedRecipeField = AccessTools.Field(typeof(InventoryGui), "m_selectedRecipe"); private static readonly FieldInfo MultiCraftingField = AccessTools.Field(typeof(InventoryGui), "m_multiCrafting"); private static readonly FieldInfo HoveredPieceField = AccessTools.Field(typeof(Hud), "m_hoveredPiece"); private static PropertyInfo _recipeProp; private static PropertyInfo _itemProp; private static readonly Dictionary<string, string> Localized = new Dictionary<string, string>(); public static void LateUpdate() { if (!Plugin.CompatChestAmounts.Value || !Plugin.ModEnabled.Value) { return; } Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null)) { if (Context.IsCraftingMenuOpen()) { Crafting(localPlayer); } else if (((Character)localPlayer).InPlaceMode()) { Building(localPlayer); } } } private static void Crafting(Player p) { InventoryGui instance = InventoryGui.instance; if ((Object)(object)instance == (Object)null || SelectedRecipeField == null || instance.m_recipeRequirementList == null) { return; } object value = SelectedRecipeField.GetValue(instance); if (value == null) { return; } if (_recipeProp == null) { Type type = value.GetType(); _recipeProp = AccessTools.Property(type, "Recipe"); _itemProp = AccessTools.Property(type, "ItemData"); if (_recipeProp == null) { return; } } object? value2 = _recipeProp.GetValue(value, null); Recipe val = (Recipe)((value2 is Recipe) ? value2 : null); if (!((Object)(object)val == (Object)null) && val.m_resources != null) { ItemData val2 = (ItemData)((_itemProp != null) ? /*isinst with value type is only supported in some contexts*/: null); int quality = ((val2 == null) ? 1 : (val2.m_quality + 1)); int multiplier = ((!(MultiCraftingField != null) || !(bool)MultiCraftingField.GetValue(instance)) ? 1 : instance.m_multiCraftAmount); Apply(instance.m_recipeRequirementList, val.m_resources, p, quality, multiplier); } } private static void Building(Player p) { Hud instance = Hud.instance; if (!((Object)(object)instance == (Object)null) && instance.m_requirementItems != null) { Piece val = (Piece)((HoveredPieceField != null) ? /*isinst with value type is only supported in some contexts*/: null); if ((Object)(object)val == (Object)null) { val = p.GetSelectedPiece(); } if (!((Object)(object)val == (Object)null) && val.m_resources != null) { Apply(instance.m_requirementItems, val.m_resources, p, 0, 1); } } } private static void Apply(GameObject[] lines, Requirement[] reqs, Player p, int quality, int multiplier) { foreach (GameObject val in lines) { if ((Object)(object)val == (Object)null || !val.activeInHierarchy) { continue; } Transform val2 = val.transform.Find("res_name"); TMP_Text val3 = (((Object)(object)val2 != (Object)null) ? ((Component)val2).GetComponent<TMP_Text>() : null); if ((Object)(object)val3 == (Object)null || string.IsNullOrEmpty(val3.text)) { continue; } foreach (Requirement val4 in reqs) { if (val4 != null && !((Object)(object)val4.m_resItem == (Object)null) && !(Localize(val4.m_resItem.m_itemData.m_shared.m_name) != val3.text)) { Patches.StyleAmount(val.transform, val4, p, quality, multiplier); break; } } } } private static string Localize(string token) { if (Localized.TryGetValue(token, out var value)) { return value; } if (Localized.Count > 2048) { Localized.Clear(); } value = GroupMenu.Localize(token); Localized[token] = value; return value; } } internal static class Context { private static readonly FieldRef<InventoryGui, Container> CurrentContainer = LinkCurrentContainer(); private static readonly FieldRef<BuildUi, List<BuildUiPieceButton>> PieceButtons = LinkPieceButtons(); private static readonly Action<InventoryGui, bool> RedrawCrafting = Util.Link<Action<InventoryGui, bool>>(AccessTools.Method(typeof(InventoryGui), "UpdateCraftingPanel", (Type[])null, (Type[])null)); private static string _menuSig; private static bool _forceRedraw; private static bool _gameRedrew; public static int MadeAvailableByChests; private static bool _menuWasCrafting; private static float _nextMenuCheck; private static int _frame = -1; private static Vector3 _framePos; private static HashSet<string> _frameGroups = Groups.Empty(); private static FieldRef<InventoryGui, Container> LinkCurrentContainer() { try { return AccessTools.FieldRefAccess<InventoryGui, Container>("m_currentContainer"); } catch { return null; } } public static bool IsCraftingMenuOpen() { if (!InventoryGui.IsVisible() || (Object)(object)InventoryGui.instance == (Object)null) { return false; } if (CurrentContainer != null) { return (Object)(object)CurrentContainer.Invoke(InventoryGui.instance) == (Object)null; } return true; } public static bool TryGet(out Vector3 origin, out HashSet<string> groups) { //IL_0001: 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_003a: Unknown result type (might be due to invalid IL or missing references) //IL_006d: 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) origin = default(Vector3); groups = null; Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return false; } if (Plugin.PullForCrafting.Value && IsCraftingMenuOpen()) { origin = ((Component)localPlayer).transform.position; groups = GroupsForCrafting(localPlayer); return groups.Count > 0; } if (Plugin.PullForBuilding.Value && ((Character)localPlayer).InPlaceMode()) { origin = ((Component)localPlayer).transform.position; groups = GroupsForBuilding(localPlayer); return groups.Count > 0; } return false; } public static HashSet<string> GroupsForCrafting(Player p) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) CraftingStation currentCraftingStation = p.GetCurrentCraftingStation(); if (!((Object)(object)currentCraftingStation != (Object)null)) { return GroupsOfStationsAround(((Component)p).transform.position); } return Groups.Of((Component)(object)currentCraftingStation); } public static HashSet<string> GroupsForBuilding(Player p) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) return GroupsOfStationsAround(((Component)p).transform.position); } private static FieldRef<BuildUi, List<BuildUiPieceButton>> LinkPieceButtons() { try { return AccessTools.FieldRefAccess<BuildUi, List<BuildUiPieceButton>>("m_pieceButtons"); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Build menu refresh is off: " + ex.Message)); return null; } } private static int RecolorBuildMenu() { BuildUi val = (((Object)(object)Hud.instance != (Object)null) ? Hud.instance.m_buildUi : null); if ((Object)(object)val == (Object)null || !((Component)val).gameObject.activeSelf || PieceButtons == null) { return 0; } List<BuildUiPieceButton> list = PieceButtons.Invoke(val); if (list == null) { return 0; } int num = 0; foreach (BuildUiPieceButton item in list) { if ((Object)(object)item != (Object)null && ((Component)item).gameObject.activeSelf) { item.UpdateRequirements(); num++; } } return num; } public static void GameRedrewCrafting() { _gameRedrew = true; } public static void RedrawSoon() { _forceRedraw = true; _nextMenuCheck = 0f; } public static void Tick() { if (Time.time < _nextMenuCheck) { return; } _nextMenuCheck = Time.time + 0.25f; Player localPlayer = Player.m_localPlayer; bool flag = (Object)(object)localPlayer != (Object)null && IsCraftingMenuOpen(); bool flag2 = (Object)(object)localPlayer != (Object)null && ((Character)localPlayer).InPlaceMode(); if ((Object)(object)localPlayer == (Object)null || !Plugin.ModEnabled.Value || (!flag && !flag2)) { _menuSig = null; return; } string text = MenuSignature(localPlayer, flag); bool forceRedraw = _forceRedraw; _forceRedraw = false; bool gameRedrew = _gameRedrew; _gameRedrew = false; if (!forceRedraw && flag && gameRedrew && _menuSig != null && flag == _menuWasCrafting) { _menuSig = text; } else { if (!forceRedraw && text == _menuSig && flag == _menuWasCrafting) { return; } bool num = !forceRedraw && (_menuSig == null || flag != _menuWasCrafting); _menuSig = text; _menuWasCrafting = flag; if (num) { return; } try { MadeAvailableByChests = 0; string text2 = null; if (flag && RedrawCrafting != null && (Object)(object)InventoryGui.instance != (Object)null) { RedrawCrafting(InventoryGui.instance, arg2: false); text2 = "crafting list"; } else if (flag2 && RecolorBuildMenu() > 0) { text2 = "build menu"; } if (text2 != null && Plugin.LogTransfers.Value) { Plugin.Log.LogInfo((object)("[Menu] Redrew the " + text2 + (forceRedraw ? " after crafting/building" : "") + ": " + $"{MadeAvailableByChests} makeable thanks to chests in range")); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Menu refresh: " + ex.Message)); } } } private static string MenuSignature(Player p, bool crafting) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) HashSet<string> hashSet = (crafting ? GroupsForCrafting(p) : GroupsForBuilding(p)); StringBuilder stringBuilder = new StringBuilder(); int num = 0; if (hashSet.Count > 0) { foreach (Container item in Chests.Eligible(((Component)p).transform.position, hashSet)) { if ((Object)(object)item != (Object)null) { stringBuilder.Append(((Object)item).GetInstanceID()).Append(','); num++; } } } if (crafting && num == 0) { return "none"; } stringBuilder.Append('|').Append(Chests.Version).Append('|') .Append(Util.InventorySig(((Humanoid)p).GetInventory())); return stringBuilder.ToString(); } public static HashSet<string> GroupsOfStationsAround(Vector3 point) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: 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_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) if (_frame == Time.frameCount && _framePos == point) { return _frameGroups; } HashSet<string> hashSet = Groups.Empty(); Registry.Stations.RemoveWhere((CraftingStation s) => (Object)(object)s == (Object)null); Vector3 val = default(Vector3); foreach (CraftingStation station in Registry.Stations) { Vector3 position = ((Component)station).transform.position; ((Vector3)(ref val))..ctor(point.x, position.y, point.z); if (Vector3.Distance(position, val) < station.GetStationBuildRange()) { hashSet.UnionWith(Groups.Of((Component)(object)station)); } } _frame = Time.frameCount; _framePos = point; _frameGroups = hashSet; return hashSet; } } internal static class EpicLootCompat { private static PropertyInfo _uiInstance; private static PropertyInfo _uiSource; private static bool _registered; private static float _nextTick; private static readonly HashSet<string> Wanted = new HashSet<string>(); public static Type TableType { get; private set; } public static Type GroupableTable { get { if (!Plugin.CompatEpicLoot.Value) { return null; } return TableType; } } public static void Init(Harmony h) { if (!Plugin.CompatEpicLoot.Value) { return; } Type type = AccessTools.TypeByName("EpicLoot.API"); Type type2 = AccessTools.TypeByName("EpicLoot_UnityLib.EnchantingTable"); Type type3 = AccessTools.TypeByName("EpicLoot_UnityLib.EnchantingTableUI"); if (type == null || type2 == null || type3 == null) { return; } TableType = type2; _uiInstance = AccessTools.Property(type3, "instance"); _uiSource = AccessTools.Property(type3, "SourceTable"); Util.Patch(h, "EnchantingTable.Start", AccessTools.Method(type2, "Start", (Type[])null, (Type[])null), typeof(EpicLootCompat), null, "TableStart"); MethodInfo methodInfo = AccessTools.Method(type, "RegisterInventoryProvider", new Type[5] { typeof(string), typeof(Func<List<ItemData>>), typeof(Func<string, int>), typeof(Func<string, int, int>), typeof(Func<ItemData, int, int>) }, (Type[])null); if (methodInfo == null || _uiInstance == null || _uiSource == null) { Plugin.Log.LogWarning((object)"Epic Loot found, but not the parts this mod uses; the enchanting table won't pull from chests."); return; } try { object obj = methodInfo.Invoke(null, new object[5] { "craftfromcontainergroups", null, new Func<string, int>(Count), new Func<string, int, int>(Remove), null }); bool flag = default(bool); int num; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } _registered = (byte)((uint)num & (flag ? 1u : 0u)) != 0; Plugin.Log.LogInfo((object)(_registered ? "Epic Loot: the enchanting table can use chests in its groups." : "Epic Loot didn't accept the chest provider; the enchanting table won't pull from chests.")); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Epic Loot: couldn't register the chest provider: " + ex.Message)); } } private static void TableStart(MonoBehaviour __instance) { Groups.RegisterRpc(Util.NView((Component)(object)__instance)); } private static Component OpenTable() { if (_uiInstance == null) { return null; } object? value = _uiInstance.GetValue(null, null); MonoBehaviour val = (MonoBehaviour)((value is MonoBehaviour) ? value : null); if ((Object)(object)val == (Object)null || !((Behaviour)val).isActiveAndEnabled) { return null; } object? value2 = _uiSource.GetValue(val, null); return (Component)((value2 is Component) ? value2 : null); } private static bool TryContext(out Player p, out HashSet<string> groups) { p = Player.m_localPlayer; groups = null; if ((Object)(object)p == (Object)null || !Plugin.ModEnabled.Value || !Plugin.CompatEpicLoot.Value) { return false; } Component val = OpenTable(); if ((Object)(object)val == (Object)null) { return false; } groups = Groups.Of(val); return groups.Count > 0; } private static int Count(string name) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrEmpty(name) || !TryContext(out var p, out var groups)) { return 0; } Wanted.Add(name); int num = 0; foreach (Container item in Chests.Eligible(((Component)p).transform.position, groups)) { if (Ownership.IsReady(item)) { num += Chests.Spare(item, name); } } return num; } private static int Remove(string name, int amount) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrEmpty(name) || amount <= 0 || !TryContext(out var p, out var groups)) { return 0; } int num = 0; foreach (Container item2 in Chests.Fresh(((Component)p).transform.position, groups)) { if (num >= amount) { break; } if (!Ownership.IsReady(item2)) { continue; } Ownership.Refresh(item2); Inventory inventory = item2.GetInventory(); int num2 = Chests.Spare(item2, name); int num3 = 0; int num4 = 0; while (num < amount && num2 > 0 && num4++ < 64) { ItemData item = inventory.GetItem(name, -1, false); if (item == null || item.m_stack <= 0) { break; } int num5 = Mathf.Min(Mathf.Min(item.m_stack, amount - num), num2); int num6 = inventory.CountItems(name, -1, true); if (!inventory.RemoveItem(item, num5)) { break; } int num7 = num6 - inventory.CountItems(name, -1, true); if (num7 <= 0) { break; } num += num7; num3 += num7; num2 -= num7; } if (num3 > 0 && Plugin.LogTransfers.Value) { Plugin.Log.LogInfo((object)$"[Transfer] Enchanting table used {num3}x {Transfer.Name(name)} from {Transfer.Describe(item2)}"); } } if (num > 0) { Chests.InvalidateCache(); } if (num < amount) { Plugin.Log.LogWarning((object)$"[Transfer] Enchanting table asked for {amount}x {Transfer.Name(name)}, chests gave {num}"); } return num; } public static void Tick() { //IL_004d: Unknown result type (might be due to invalid IL or missing references) if (!_registered || Time.time < _nextTick) { return; } _nextTick = Time.time + 1f; if (!TryContext(out var p, out var groups)) { Wanted.Clear(); } else { if (Wanted.Count == 0) { return; } foreach (Container item in Chests.Eligible(((Component)p).transform.position, groups)) { if (Ownership.IsReady(item)) { continue; } foreach (string item2 in Wanted) { if (Chests.Spare(item, item2) > 0) { Ownership.Request(item); break; } } } } } } internal static class GroundPickup { private static readonly FieldInfo InstancesField = AccessTools.Field(typeof(ItemDrop), "s_instances"); private static readonly Action<ItemDrop> SaveFn = Util.Link<Action<ItemDrop>>(AccessTools.Method(typeof(ItemDrop), "Save", (Type[])null, (Type[])null)); private static readonly Func<ItemDrop, double> AgeFn = Util.Link<Func<ItemDrop, double>>(AccessTools.Method(typeof(ItemDrop), "GetTimeSinceSpawned", (Type[])null, (Type[])null)); private static readonly List<Container> _chests = new List<Container>(); private static readonly List<ItemDrop> _nearDrops = new List<ItemDrop>(); private static readonly List<ItemDrop> _candidates = new List<ItemDrop>(); private static readonly Dictionary<string, List<Container>> _holders = new Dictionary<string, List<Container>>(); private static readonly Stack<List<Container>> _spareLists = new Stack<List<Container>>(); private const float MinAgeSeconds = 2f; private static float _next; private static int _cDroppedByPlayer; private static int _cTooYoung; private static int _cNoChest; private static int _cWaitChest; private static int _cWaitItem; private static int _cOther; private static int _cStored; private static string _lastSummary; private static readonly HashSet<Container> _receivers = new HashSet<Container>(); private static float _lastSummaryTime; public static void Tick() { //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_023c: Unknown result type (might be due to invalid IL or missing references) //IL_0241: Unknown result type (might be due to invalid IL or missing references) //IL_0243: Unknown result type (might be due to invalid IL or missing references) //IL_0248: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.ModEnabled.Value || !Plugin.GroundPickupEnabled.Value || Time.time < _next) { return; } _next = Time.time + Mathf.Max(0.5f, Plugin.GroundPickupInterval.Value); Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || (Object)(object)ZNetScene.instance == (Object)null || !(InstancesField?.GetValue(null) is List<ItemDrop> { Count: not 0 } list)) { return; } float value = Plugin.GroundPickupRadius.Value; float num = Plugin.VisualRange.Value + value; float num2 = num * num; Vector3 position = ((Component)localPlayer).transform.position; List<Container> chests = _chests; chests.Clear(); Vector3 val; foreach (Container item in Chests.AllLoaded()) { if (!((Object)(object)item == (Object)null)) { val = position - ((Component)item).transform.position; if (!(((Vector3)(ref val)).sqrMagnitude > num2) && !((Object)(object)Util.PieceOf((Component)(object)item) == (Object)null) && (!Plugin.GroundPickupOnlyMyChests.Value || Groups.BuiltByMe((Component)(object)item)) && (!Plugin.GroundPickupOnlyGroupedChests.Value || Groups.Of((Component)(object)item).Count != 0) && item.GetInventory() != null && !item.IsInUse() && !Util.VehicleBusy((Component)(object)item)) { chests.Add(item); } } } _cDroppedByPlayer = (_cTooYoung = (_cNoChest = (_cWaitChest = (_cWaitItem = (_cOther = (_cStored = 0)))))); int num3 = 0; if (chests.Count == 0) { if (Plugin.LogTransfers.Value) { Summary("no chests qualify (built chests within range" + (Plugin.GroundPickupOnlyGroupedChests.Value ? ", in a group" : "") + (Plugin.GroundPickupOnlyMyChests.Value ? ", built by you" : "") + ")"); } return; } _receivers.Clear(); _nearDrops.Clear(); foreach (ItemDrop item2 in list) { if ((Object)(object)item2 != (Object)null) { val = ((Component)item2).transform.position - position; if (((Vector3)(ref val)).sqrMagnitude <= num2) { _nearDrops.Add(item2); } } } num3 = _nearDrops.Count; _candidates.Clear(); foreach (ItemDrop nearDrop in _nearDrops) { try { if (Candidate(nearDrop)) { _candidates.Add(nearDrop); } } catch (Exception ex) { Plugin.Log.LogError((object)("[Ground] " + ex.Message)); } } _nearDrops.Clear(); if (_candidates.Count > 0) { IndexHolders(chests); foreach (ItemDrop candidate in _candidates) { try { TryStore(candidate, value); } catch (Exception ex2) { Plugin.Log.LogError((object)("[Ground] " + ex2.Message)); } } } _candidates.Clear(); if (_cStored > 0) { Chests.InvalidateCache(); } PickupFx.BatchSound(_receivers); if (Plugin.LogTransfers.Value) { if (num3 == 0) { Summary($"{chests.Count} chests ready, no items on the ground nearby"); } else { Summary($"{chests.Count} chests, {num3} ground items: stored {_cStored}, " + $"player-dropped (setting off) {_cDroppedByPlayer}, just landed {_cTooYoung}, " + $"no matching chest in range {_cNoChest}, waiting for chest {_cWaitChest}, " + $"waiting for item {_cWaitItem}, other {_cOther}"); } } } private static void Summary(string text) { if (Plugin.LogTransfers.Value && (!(text == _lastSummary) || !(Time.time - _lastSummaryTime < 30f))) { _lastSummary = text; _lastSummaryTime = Time.time; Plugin.Log.LogInfo((object)("[Ground] " + text)); } } private static void IndexHolders(List<Container> chests) { foreach (List<Container> value2 in _holders.Values) { value2.Clear(); _spareLists.Push(value2); } _holders.Clear(); foreach (Container chest in chests) { foreach (ItemData allItem in chest.GetInventory().GetAllItems()) { string name = allItem.m_shared.m_name; if (!_holders.TryGetValue(name, out var value)) { value = ((_spareLists.Count > 0) ? _spareLists.Pop() : new List<Container>()); _holders[name] = value; } if (value.Count == 0 || (Object)(object)value[value.Count - 1] != (Object)(object)chest) { value.Add(chest); } } } } private static bool Candidate(ItemDrop drop) { if ((Object)(object)drop == (Object)null) { return false; } if (!drop.m_autoPickup && !Plugin.GroundPickupPlayerDropped.Value) { _cDroppedByPlayer++; return false; } ZNetView component = ((Component)drop).GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null || !component.IsValid()) { _cOther++; return false; } if (drop.IsPiece() || drop.InTar()) { _cOther++; return false; } if ((Object)(object)((Component)drop).GetComponent<Fish>() != (Object)null) { _cOther++; return false; } ItemData itemData = drop.m_itemData; if (itemData == null || itemData.m_shared == null) { _cOther++; return false; } if (itemData.m_shared.m_questItem) { _cOther++; return false; } if (Plugin.GroundPickupStackablesOnly.Value && itemData.m_shared.m_maxStackSize <= 1) { _cOther++; return false; } if (Age(drop) < 2.0) { _cTooYoung++; return false; } return true; } private static void TryStore(ItemDrop drop, float radius) { //IL_0048: 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_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_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_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_01b1: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)drop == (Object)null) { return; } ItemData itemData = drop.m_itemData; if (itemData == null || itemData.m_shared == null) { return; } string name = itemData.m_shared.m_name; Container val = null; if (_holders.TryGetValue(name, out var value)) { float num = radius * radius; Vector3 position = ((Component)drop).transform.position; foreach (Container item in value) { if ((Object)(object)item == (Object)null) { continue; } Vector3 val2 = ((Component)item).transform.position - position; float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude; if (!(sqrMagnitude > num)) { Inventory inventory = item.GetInventory(); if (inventory.CanAddItem(itemData, itemData.m_stack) || inventory.CanAddItem(itemData, 1)) { val = item; num = sqrMagnitude; } } } } if ((Object)(object)val == (Object)null) { _cNoChest++; return; } if (!Ownership.IsReady(val)) { _cWaitChest++; Ownership.Request(val); return; } if (!drop.CanPickup(true)) { _cWaitItem++; drop.RequestOwn(); return; } Ownership.Refresh(val); Vector3 position2 = ((Component)drop).transform.position; drop.Load(); itemData = drop.m_itemData; int stack = itemData.m_stack; if (stack <= 0 || !val.GetInventory().HaveItem(name, false)) { return; } int num2 = Transfer.AddCounted(val.GetInventory(), itemData, stack); if (num2 <= 0) { _cOther++; return; } _cStored++; _receivers.Add(val); List<string> list = new List<string>(Groups.Of((Component)(object)val)); list.Sort(StringComparer.OrdinalIgnoreCase); PickupFx.Fly(position2, val, (list.Count > 0) ? list[0] : ""); if (num2 >= stack) { ZNetScene.instance.Destroy(((Component)drop).gameObject); } else { itemData.m_stack = stack - num2; SaveFn?.Invoke(drop); } if (Plugin.LogTransfers.Value) { Plugin.Log.LogInfo((object)($"[Ground] {num2}x {Transfer.Name(name)} -> {Transfer.Describe(val)}" + ((num2 < stack) ? $" ({stack - num2} left on the ground, chest full)" : ""))); } } private static double Age(ItemDrop drop) { if (AgeFn == null) { return 2.0; } try { return AgeFn(drop); } catch { return 2.0; } } } internal static class GroupMenu { private enum Kind { Containers, Stations, Smelters, Cooking, Fires } [CompilerGenerated] private static class <>O { public static WindowFunction <0>__Draw; public static Func<string, string> <1>__Name; } private static int _closedFrame = -10; private const int WindowId = 1262700103; private const float ObjectWidth = 380f; private const float SideWidth = 96f; private const float OverviewWidth = 260f; private static Rect _rect = new Rect(0f, 0f, 380f, 10f); private static bool _needsCenter = true; private static ZNetView _target; private static Component _targetComp; private static string _targetName; private static Player _player; private static string _playerName; private static bool _hadPlayer; private static string _newGroup = ""; private static bool _hadTarget; private static readonly float[] Radii = new float[3] { 3f, 6f, 10f }; private static int _radiusIndex = 1; private static string _defaultEdit; private static readonly Dictionary<string, KeyValuePair<bool, float>> Optimistic = new Dictionary<string, KeyValuePair<bool, float>>(StringComparer.OrdinalIgnoreCase); private static MethodInfo _localizeMethod; private static Func<object> _localizationInstance; private static bool _localizeLookedUp; private static GUIStyle _hint; private static bool _manage; private static string _renaming; private static string _renameText = ""; private static string _deleting; private static readonly KeyValuePair<Kind, string>[] Kinds = new KeyValuePair<Kind, string>[5] { new KeyValuePair<Kind, string>(Kind.Containers, "Containers"), new KeyValuePair<Kind, string>(Kind.Stations, "Crafting stations"), new KeyValuePair<Kind, string>(Kind.Smelters, "Smelters & machines"), new KeyValuePair<Kind, string>(Kind.Cooking, "Cooking & brewing"), new KeyValuePair<Kind, string>(Kind.Fires, "Fires & lights") }; private static int _nearbyFrame = -1; private static readonly Dictionary<Kind, List<Component>> _nearby = new Dictionary<Kind, List<Component>>(); private static bool _planOptimistic; private static float _planOptimisticTime = -10f; private static GUIStyle _wrapToggle; private static bool _autoOptimistic; private static float _autoOptimisticTime = -10f; private static string _orderText = ""; private static int _orderOptimistic = -1; private static float _orderOptimisticTime = -10f; private static bool _keepOneOptimistic; private static float _keepOneOptimisticTime = -10f; private static bool _dropOptimistic; private static float _dropOptimisticTime = -10f; public static bool IsOpen { get; private set; } public static bool BlockingInput { get { if (!IsOpen) { return Time.frameCount <= _closedFrame + 1; } return true; } } private static float Width { get { if (!((Object)(object)_target != (Object)null) && !((Object)(object)_player != (Object)null)) { return 260f; } return 380f; } } private static float Scale { get { float num = Plugin.UiScale.Value; if (num <= 0.01f) { num = Mathf.Max(1f, (float)Screen.height / 1080f); } return Mathf.Clamp(num, 0.5f, 4f); } } public static void Update() { //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0136: 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_0074: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; KeyboardShortcut value; if (IsOpen) { bool flag = (_hadTarget && ((Object)(object)_target == (Object)null || !_target.IsValid())) || (_hadPlayer && (Object)(object)_player == (Object)null); bool flag2 = ((Object)(object)_target != (Object)null && (Object)(object)localPlayer != (Object)null && Vector3.Distance(((Component)localPlayer).transform.position, ((Component)_target).transform.position) > localPlayer.m_maxInteractDistance + 2f) || ((Object)(object)_player != (Object)null && (Object)(object)localPlayer != (Object)null && Vector3.Distance(((Component)localPlayer).transform.position, ((Component)_player).transform.position) > 10f); if ((Object)(object)localPlayer == (Object)null || Input.GetKeyDown((KeyCode)27) || flag || flag2) { Close(); return; } value = Plugin.MenuKey.Value; if (((KeyboardShortcut)(ref value)).IsDown() && GUIUtility.keyboardControl == 0) { Close(); } } else if (!((Object)(object)localPlayer == (Object)null) && Plugin.ModEnabled.Value && !IsTypingOrInMenu()) { value = Plugin.MenuKey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { Open(localPlayer); } } } private static bool IsTypingOrInMenu() { if ((!((Object)(object)Chat.instance != (Object)null) || !Chat.instance.HasFocus()) && !Console.IsVisible() && !TextInput.IsVisible() && !InventoryGui.IsVisible() && !Minimap.IsOpen() && !Menu.IsVisible()) { return StoreGui.IsVisible(); } return true; } private static void Open(Player p) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) _target = null; _targetComp = null; _targetName = null; _player = null; GameObject val = ((Humanoid)p).GetHoverObject(); Player val2 = (((Object)(object)val != (Object)null) ? val.GetComponentInParent<Player>() : null); if ((Object)(object)val2 == (Object)null) { Character hoverCreature = p.GetHoverCreature(); Player val3 = (Player)(object)((hoverCreature is Player) ? hoverCreature : null); if (val3 != null && Vector3.Distance(((Component)p).transform.position, ((Component)val3).transform.position) < 8f) { val2 = val3; } } if ((Object)(object)val2 != (Object)null && (Object)(object)val2 != (Object)(object)p) { _player = val2; _playerName = val2.GetPlayerName(); val = null; } if ((Object)(object)val != (Object)null) { Component val4 = FindTaggable(val); if ((Object)(object)val4 != (Object)null) { _target = Util.NView(val4); _targetComp = val4; Piece componentInParent = val4.GetComponentInParent<Piece>(); _targetName = (((Object)(object)componentInParent != (Object)null) ? Localize(componentInParent.m_name) : ((Object)val4.gameObject).name.Replace("(Clone)", "")); } else { Plugin.Log.LogInfo((object)("[Menu] Looking at something with no chest/station/smelter: " + Describe(val))); } } if ((Object)(object)_target != (Object)null && !_target.IsValid()) { _target = null; } _hadTarget = (Object)(object)_target != (Object)null; _hadPlayer = (Object)(object)_player != (Object)null; Optimistic.Clear(); _autoOptimisticTime = -10f; _orderOptimisticTime = -10f; _keepOneOptimisticTime = -10f; _dropOptimisticTime = -10f; _orderText = ""; _renaming = null; _deleting = null; _newGroup = ""; _defaultEdit = null; _needsCenter = true; IsOpen = true; } internal static Component FindTaggable(GameObject hover) { if ((Object)(object)hover == (Object)null) { return null; } Component componentInParent = (Component)(object)hover.GetComponentInParent<Container>(); if ((Object)(object)componentInParent == (Object)null) { componentInParent = (Component)(object)hover.GetComponentInParent<Smelter>(); } if ((Object)(object)componentInParent == (Object)null) { componentInParent = (Component)(object)hover.GetComponentInParent<CraftingStation>(); } if ((Object)(object)componentInParent == (Object)null) { componentInParent = (Component)(object)hover.GetComponentInParent<CookingStation>(); } if ((Object)(object)componentInParent == (Object)null) { componentInParent = (Component)(object)hover.GetComponentInParent<Fermenter>(); } if ((Object)(object)componentInParent == (Object)null) { componentInParent = (Component)(object)hover.GetComponentInParent<Fireplace>(); } if ((Object)(object)componentInParent == (Object)null) { componentInParent = (Component)(object)hover.GetComponentInParent<ShieldGenerator>(); } if ((Object)(object)componentInParent == (Object)null) { componentInParent = (Component)(object)hover.GetComponentInParent<Turret>(); } Type groupableTable = EpicLootCompat.GroupableTable; if ((Object)(object)componentInParent == (Object)null && groupableTable != null) { componentInParent = hover.GetComponentInParent(groupableTable); } if ((Object)(object)componentInParent != (Object)null) { return componentInParent; } ZNetView componentInParent2 = hover.GetComponentInParent<ZNetView>(); if ((Object)(object)componentInParent2 == (Object)null) { return null; } componentInParent = (Component)(object)((Component)componentInParent2).GetComponentInChildren<Smelter>(true); if ((Object)(object)componentInParent == (Object)null) { componentInParent = (Component)(object)((Component)componentInParent2).GetComponentInChildren<CraftingStation>(true); } if ((Object)(object)componentInParent == (Object)null) { componentInParent = (Component)(object)((Component)componentInParent2).GetComponentInChildren<CookingStation>(true); } if ((Object)(object)componentInParent == (Object)null) { componentInParent = (Component)(object)((Component)componentInParent2).GetComponentInChildren<Fermenter>(true); } if ((Object)(object)componentInParent == (Object)null) { componentInParent = (Component)(object)((Component)componentInParent2).GetComponentInChildren<Fireplace>(true); } if ((Object)(object)componentInParent == (Object)null) { componentInParent = (Component)(object)((Component)componentInParent2).GetComponentInChildren<ShieldGenerator>(true); } if ((Object)(object)componentInParent == (Object)null) { componentInParent = (Component)(object)((Component)componentInParent2).GetComponentInChildren<Turret>(true); } if ((Object)(object)componentInParent == (Object)null && groupableTable != null) { componentInParent = ((Component)componentInParent2).GetComponentInChildren(groupableTable, true); } if ((Object)(object)componentInParent == (Object)null) { componentInParent = (Component)(object)((Component)componentInParent2).GetComponentInChildren<Container>(true); } return componentInParent; } private static string Describe(GameObject go) { StringBuilder stringBuilder = new StringBuilder(); Transform val = go.transform; int num = 0; while ((Object)(object)val != (Object)null && num < 6) { if (num > 0) { stringBuilder.Append(" <- "); } stringBuilder.Append(((Object)val).name).Append(" ["); Component[] components = ((Component)val).GetComponents<Component>(); foreach (Component val2 in components) { if ((Object)(object)val2 != (Object)null && !(val2 is Transform)) { stringBuilder.Append(((object)val2).GetType().Name).Append(' '); } } stringBuilder.Append(']'); num++; val = val.parent; } return stringBuilder.ToString(); } private static void Close() { IsOpen = false; _closedFrame = Time.frameCount; _target = null; _targetComp = null; _player = null; GUIUtility.keyboardControl = 0; } internal static string Localize(string token) { try { if (!_localizeLookedUp) { _localizeLookedUp = true; Type type = AccessTools.TypeByName("Localization"); if (type != null) { _localizeMethod = AccessTools.Method(type, "Localize", new Type[1] { typeof(string) }, (Type[])null); PropertyInfo prop = AccessTools.Property(type, "instance"); FieldInfo field = AccessTools.Field(type, "m_instance"); if (prop != null) { _localizationInstance = () => prop.GetValue(null, null); } else if (field != null) { _localizationInstance = () => field.GetValue(null); } } } object obj = _localizationInstance?.Invoke(); if (_localizeMethod != null && obj != null) { return (string)_localizeMethod.Invoke(obj, new object[1] { token }); } } catch { } return token.TrimStart(new char[1] { '$' }); } public static void OnGUI() { //IL_001a: 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_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_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_0103: 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_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Expected O, but got Unknown if (IsOpen) { Cursor.lockState = (CursorLockMode)0; Cursor.visible = true; float scale = Scale; Matrix4x4 matrix = GUI.matrix; GUI.matrix = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(scale, scale, 1f)); float num = (float)Screen.width / scale; float num2 = (float)Screen.height / scale; if (_needsCenter) { _rect = new Rect(num / 2f - Width / 2f, num2 * 0.25f, Width, 10f); _needsCenter = false; } ((Rect)(ref _rect)).width = Width; ((Rect)(ref _rect)).height = 10f; string text = (((Object)(object)_player != (Object)null) ? "Add to groups" : (((Object)(object)_target != (Object)null) ? _targetName : "Craft From Container Groups")); Rect rect = _rect; object obj = <>O.<0>__Draw; if (obj == null) { WindowFunction val = Draw; <>O.<0>__Draw = val; obj = (object)val; } _rect = GUILayout.Window(1262700103, rect, (WindowFunction)obj, text, Array.Empty<GUILayoutOption>()); ((Rect)(ref _rect)).x = Mathf.Clamp(((Rect)(ref _rect)).x, 0f, Mathf.Max(0f, num - ((Rect)(ref _rect)).width)); ((Rect)(ref _rect)).y = Mathf.Clamp(((Rect)(ref _rect)).y, 0f, Mathf.Max(0f, num2 - 40f)); GUI.matrix = matrix; } } private static void Draw(int id) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Expected O, but got Unknown if (_hint == null) { _hint = new GUIStyle(GUI.skin.label) { wordWrap = true, richText = true }; } if ((Object)(object)_player != (Object)null) { DrawPlayer(); } else if ((Object)(object)_target != (Object)null) { DrawObject(); } else { DrawOverview(); } GUI.DragWindow(); } private static void DrawOverview() { if (GUILayout.Button((Plugin.ShowMarkers.Value ? "[x]" : "[ ]") + " Show group markers", Array.Empty<GUILayoutOption>())) { Plugin.ShowMarkers.Value = !Plugin.ShowMarkers.Value; Visuals.ForceRefresh(); } if (GUILayout.Button((Plugin.ShowLinks.Value ? "[x]" : "[ ]") + " Show what pulls from what", Array.Empty<GUILayoutOption>())) { Plugin.ShowLinks.Value = !Plugin.ShowLinks.Value; Visuals.ForceRefresh(); } if (GUILayout.Button((Plugin.GroundPickupEnabled.Value ? "[x]" : "[ ]") + " Chests pick up matching items nearby", Array.Empty<GUILayoutOption>())) { Plugin.GroundPickupEnabled.Value = !Plugin.GroundPickupEnabled.Value; } GUILayout.Space(6f); GUILayout.Label("New chests I build go in:", _hint, Array.Empty<GUILayoutOption>()); if (_defaultEdit == null) { _defaultEdit = string.Join(", ", Groups.Parse(Plugin.DefaultGroups.Value).Select(Groups.Name).ToArray()); } GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); _defaultEdit = GUILayout.TextField(_defaultEdit, 48, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); if (GUILayout.Button("Save", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(48f) })) { List<string> list = new List<string>(); string[] array = _defaultEdit.Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { string text = Groups.Create(array[i]); if (text.Length > 0 && !list.Contains(text)) { list.Add(text); } } Plugin.DefaultGroups.Value = string.Join(",", list.ToArray()); _defaultEdit = null; GUIUtility.keyboardControl = 0; } GUILayout.EndHorizontal(); HashSet<string> hashSet = Groups.Parse(Plugin.DefaultGroups.Value); if (hashSet.Count > 0) { List<Container> list2 = new List<Container>(); foreach (Container item in Chests.AllLoaded()) { if (Groups.BuiltByMe((Component)(object)item) && !Groups.Of((Component)(object)item).IsSupersetOf(hashSet)) { list2.Add(item); } } GUI.enabled = list2.Count > 0; if (GUILayout.Button($"Add my {list2.Count} loaded chests to it", Array.Empty<GUILayoutOption>())) { int num = Groups.Bulk(list2.Cast<Component>(), hashSet, add: true); Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)2, $"Updated {num} chests", 0, (Sprite)null, false); } Chests.InvalidateCache(); Visuals.ForceRefresh(); } GUI.enabled = true; } GUILayout.Space(6f); if (GUILayout.Button((_manage ? "[-]" : "[+]") + " Manage groups", Array.Empty<GUILayoutOption>())) { _manage = !_manage; _renaming = null; _deleting = null; } if (_manage) { DrawManage(); } } private static void DrawManage() { //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Invalid comparison between Unknown and I4 //IL_0208: Unknown result type (might be due to invalid IL or missing references) //IL_020d: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Invalid comparison between Unknown and I4 //IL_0236: Unknown result type (might be due to invalid IL or missing references) //IL_0238: Unknown result type (might be due to invalid IL or missing references) List<string> list = Groups.Known(); if (list.Count == 0) { GUILayout.Label("No groups yet.", _hint, Array.Empty<GUILayoutOption>()); return; } string text = null; string text2 = null; foreach (string item in list) { string text3 = Groups.Name(item); string text4 = Groups.SharedBy(item); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); if (text4 != null) { GUILayout.Label("<color=#" + ColorUtility.ToHtmlStringRGB(Brighten(Visuals.ColorFor(item))) + ">" + text3 + "</color> <color=#999999>from " + text4 + "</color>", _hint, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); } else if (_renaming == item) { GUI.SetNextControlName("cfcg_rename"); _renameText = GUILayout.TextField(_renameText, 24, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); bool flag = (int)Event.current.type == 4 && (int)Event.current.keyCode == 13 && GUI.GetNameOfFocusedControl() == "cfcg_rename"; if (GUILayout.Button("OK", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(36f) }) || flag) { text = item; } if (GUILayout.Button("X", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(26f) })) { _renaming = null; } } else if (_deleting == item) { GUILayout.Label("Delete " + text3 + "?", _hint, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); if (GUILayout.Button("Yes", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(40f) })) { text2 = item; } if (GUILayout.Button("No", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(36f) })) { _deleting = null; } } else { Color c = Visuals.ColorFor(item); string text5 = (Groups.IsPrivate(item) ? "" : " <color=#999999>(public)</color>"); GUILayout.Label("<color=#" + ColorUtility.ToHtmlStringRGB(Brighten(c)) + ">" + text3 + "</color>" + text5, _hint, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); if (GUILayout.Button("Rename", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(62f) })) { _renaming = item; _renameText = text3; _deleting = null; } if (GUILayout.Button("Delete", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(56f) })) { _deleting = item; _renaming = null; } } GUILayout.EndHorizontal(); } if (Groups.HasPublicGroups()) { GUILayout.Label("<color=#999999>Public groups can be seen and used by everyone with this mod.</color>", _hint, Array.Empty<GUILayoutOption>()); if (GUILayout.Button("Make my public groups private", Array.Empty<GUILayoutOption>())) { int num = Groups.MakePublicGroupsPrivate(); Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)2, $"Made {num} groups private", 0, (Sprite)null, false); } _defaultEdit = null; } } if (text != null) { string text6 = _renameText.Trim(); if (text6.Length > 0 && !text6.Equals(Groups.Name(text), StringComparison.OrdinalIgnoreCase)) { bool flag2 = Groups.IsMine(text); int num2 = Groups.Rename(text, text6); Player localPlayer2 = Player.m_localPlayer; if (localPlayer2 != null) { ((Character)localPlayer2).Message((MessageType)2, flag2 ? ("Renamed to " + text6) : $"Renamed on {num2} things nearby", 0, (Sprite)null, false); } _defaultEdit = null; } _renaming = null; GUIUtility.keyboardControl = 0; } if (text2 != null) { int num3 = Groups.Rename(text2, ""); Player localPlayer3 = Player.m_localPlayer; if (localPlayer3 != null) { ((Character)localPlayer3).Message((MessageType)2, $"Deleted from {num3} things nearby", 0, (Sprite)null, false); } _deleting = null; _defaultEdit = null; } } private static void DrawNearbyRow(HashSet<string> current) { //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase); foreach (string item in Groups.Known()) { if (IsIn(current, item)) { hashSet.Add(item); } } GUILayout.Space(8f); if (hashSet.Count == 0) { GUILayout.Label("<color=#999999>Give this a group to add nearby things to it.</color>", _hint, Array.Empty<GUILayoutOption>()); return; } GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label("Add nearby to " + string.Join(", ", hashSet.Select(Groups.Name).ToArray()) + ":", _hint, Array.Empty<GUILayoutOption>()); for (int i = 0; i < Radii.Length; i++) { Color backgroundColor = GUI.backgroundColor; if (i == _radiusIndex) { GUI.backgroundColor = new Color(0.35f, 0.85f, 0.45f); } if (GUILayout.Button($"{Radii[i]:0}m", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(40f) })) { _radiusIndex = i; } GUI.backgroundColor = backgroundColor; } GUILayout.EndHorizontal(); KeyValuePair<Kind, string>[] kinds = Kinds; for (int j = 0; j < kinds.Length; j++) { KeyValuePair<Kind, string> keyValuePair = kinds[j]; Kind key = keyValuePair.Key; string value = keyValuePair.Value; List<Component> list = Nearby(key); int num = 0; int num2 = 0; foreach (Component item2 in list) { HashSet<string> hashSet2 = Groups.Of(item2); if (!hashSet2.IsSupersetOf(hashSet)) { num++; } if (hashSet2.Overlaps(hashSet)) { num2++; } } GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label($"{value} ({list.Count})", _hint, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUI.enabled = num > 0; if (GUILayout.Button($"+{num}", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(44f) })) { Report(Groups.Bulk(list, hashSet, add: true), "Added", value); } GUI.enabled = num2 > 0; if (GUILayout.Button($"-{num2}", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(44f) })) { Report(Groups.Bulk(list, hashSet, add: false), "Removed", value); } GUI.enabled = true; GUILayout.EndHorizontal(); } bool flag = GUILayout.Toggle(Plugin.BulkOnlyMine.Value, " Only things I built", Array.Empty<GUILayoutOption>()); if (flag != Plugin.BulkOnlyMine.Value) { Plugin.BulkOnlyMine.Value = flag; } } private static List<Component> Nearby(Kind kind) { //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_01b6: 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) if (_nearbyFrame != Time.frameCount) { _nearbyFrame = Time.frameCount; _nearby.Clear(); } if (_nearby.TryGetValue(kind, out var value)) { return value; } List<Component> list = new List<Component>(); _nearby[kind] = list; if ((Object)(object)_target == (Object)null) { return list; } IEnumerable<Component> enumerable; switch (kind) { case Kind.Containers: enumerable = Chests.AllLoaded().Cast<Component>(); break; case Kind.Stations: Registry.Stations.RemoveWhere((CraftingStation x) => (Object)(object)x == (Object)null); enumerable = Registry.Stations.Cast<Component>(); break; case Kind.Smelters: Registry.Smelters.RemoveWhere((MonoBehaviour x) => (Object)(object)x == (Object)null); enumerable = Registry.Smelters.Cast<Component>(); break; case Kind.Cooking: Registry.Cooking.RemoveWhere((MonoBehaviour x) => (Object)(object)x == (Object)null); enumerable = Registry.Cooking.Cast<Component>(); break; default: Registry.Fires.RemoveWhere((MonoBehaviour x) => (Object)(object)x == (Object)null); enumerable = Registry.Fires.Cast<Component>(); break; } Vector3 position = ((Component)_target).transform.position; float num = Radii[Mathf.Clamp(_radiusIndex, 0, Radii.Length - 1)]; foreach (Component item in enumerable) { if (!((Object)(object)item == (Object)null) && !((Object)(object)Util.NView(item) == (Object)(object)_target) && !(Vector3.Distance(position, item.transform.position) > num) && !((Object)(object)Util.PieceOf(item) == (Object)null) && (!Plugin.BulkOnlyMine.Value || Groups.BuiltByMe(item))) { list.Add(item); } } return list; } private static void Report(int n, string verb, string what) { Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)2, $"{verb}: {n} {what.ToLowerInvariant()}", 0, (Sprite)null, false); } Chests.InvalidateCache(); Visuals.ForceRefresh(); _nearbyFrame = -1; } private static void DrawObject() { //IL_0060: 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_0149: 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_0198: Invalid comparison between Unknown and I4 //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01a6: Invalid comparison between Unknown and I4 //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_02a2: Unknown result type (might be due to invalid IL or missing references) //IL_02a7: Unknown result type (might be due to invalid IL or missing references) //IL_02b9: Unknown result type (might be due to invalid IL or missing references) //IL_02bb: Unknown result type (might be due to invalid IL or missing references) HashSet<string> current = Groups.Of(_target); List<string> list = Groups.Known(); int num = Mathf.Max(1, Plugin.ButtonsPerRow.Value); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(260f) }); if (list.Count == 0) { GUILayout.Label("No groups yet: type a name below.", _hint, Array.Empty<GUILayoutOption>()); } Color backgroundColor = GUI.backgroundColor; for (int i = 0; i < list.Count; i += num) { GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); for (int j = i; j < i + num; j++) { if (j >= list.Count) { GUILayout.Label("", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); continue; } string text = list[j]; bool flag = IsIn(current, text); GUI.backgroundColor = (Color)(flag ? new Color(0.35f, 0.85f, 0.45f) : backgroundColor); GUI.enabled = Groups.CanEdit(text, _targetComp); if (GUILayout.Button(Groups.Name(text), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) })) { SetMembership(text, !flag); } GUI.enabled = true; } GUI.backgroundColor = backgroundColor; GUILayout.EndHorizontal(); } GUI.backgroundColor = backgroundColor; GUILayout.Space(4f); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUI.SetNextControlName("cfcg_new"); _newGroup = GUILayout.TextField(_newGroup, 24, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); bool flag2 = (int)Event.current.type == 4 && (int)Event.current.keyCode == 13 && GUI.GetNameOfFocusedControl() == "cfcg_new"; if ((GUILayout.Button("+ New", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(60f) }) || flag2) && _newGroup.Trim().Length > 0) { string text2 = Groups.Create(_newGroup); if (text2.Length > 0) { SetMembership(text2, on: true); } _newGroup = ""; GUIUtility.keyboardControl = 0; } GUILayout.EndHorizontal(); DrawKeepFilled(); DrawLeaveOne(); DrawDropFood(); DrawNearbyRow(current); GUILayout.EndVertical(); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(96f) }); GUILayout.BeginVertical(GUI.skin.box, Array.Empty<GUILayoutOption>()); GUILayout.Label("In:", _hint, Array.Empty<GUILayoutOption>()); bool flag3 = false; foreach (string item in list) { if (IsIn(current, item)) { flag3 = true; Color c = Visuals.ColorFor(item); GUILayout.Label("<color=#" + ColorUtility.ToHtmlStringRGB(Brighten(c)) + ">" + Groups.Name(item) + "</color>", _hint, Array.Empty<GUILayoutOption>()); } } if (!flag3) { GUILayout.Label("<color=#999999>none</color>", _hint, Array.Empty<GUILayoutOption>()); } if (Groups.IsWaiting(_target)) { string text3 = Chests.PlayerName(_target.GetZDO().GetOwner()); GUILayout.Label("<color=#d8b04a>Saves when " + text3 + " leaves</color>", _hint, Array.Empty<GUILayoutOption>()); } GUILayout.EndVertical(); DrawPlanBuildSwitch(); GUILayout.EndVertical(); GUILayout.EndHorizontal(); } private static void DrawPlanBuildSwitch() { //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_003c: Expected O, but got Unknown if (!PlanBuildCompat.Active || (Object)(object)((Component)_target).GetComponent<Container>() == (Object)null) { return; } if (_wrapToggle == null) { _wrapToggle = new GUIStyle(GUI.skin.toggle) { wordWrap = true }; } bool flag = ((Time.time - _planOptimisticTime < 3f) ? _planOptimistic : (Groups.GetInt(_target, "cfcg_planbuild") != 0)); GUILayout.BeginVertical(GUI.skin.box, Array.Empty<GUILayoutOption>()); bool flag2 = GUILayout.Toggle(flag, " Pullable from PlanBuild", _wrapToggle, Array.Empty<GUILayoutOption>()); GUILayout.EndVertical(); if (flag2 != flag) { if (Groups.SetInt(_target, "cfcg_planbuild", flag2 ? 1 : 0)) { _planOptimisticTime = -10f; return; } _planOptimistic = flag2; _planOptimisticTime = Time.time; } } private static void DrawPlayer() { //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Invalid comparison between Unknown and I4 //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Invalid comparison between Unknown and I4 //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: 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) long playerID = _player.GetPlayerID(); List<string> list = Groups.MyPrivate(); int num = Mathf.Max(1, Plugin.ButtonsPerRow.Value); GUILayout.Label("<b>" + _playerName + "</b>", _hint, Array.Empty<GUILayoutOption>()); if (list.Count == 0) { GUILayout.Label("No private groups yet: type a name below.", _hint, Array.Empty<GUILayoutOption>()); } string text = null; Color backgroundColor = GUI.backgroundColor; for (int i = 0; i < list.Count; i += num) { GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); for (int j = i; j < i + num; j++) { if (j >= list.Count) { GUILayout.Label("", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); continue; } string text2 = list[j]; GUI.backgroundColor = (Color)(Groups.IsSharedWith(text2, playerID) ? new Color(0.35f, 0.85f, 0.45f) : backgroundColor); if (GUILayout.Button(Groups.Name(text2), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) })) { text = text2; } } GUI.backgroundColor = backgroundColor; GUILayout.EndHorizontal(); } GUI.backgroundColor = backgroundColor; GUILayout.Space(4f); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUI.SetNextControlName("cfcg_new"); _newGroup = GUILayout.TextField(_newGroup, 24, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); bool flag = (int)Event.current.type == 4 && (int)Event.current.keyCode == 13 && GUI.GetNameOfFocusedControl() == "cfcg_new"; string text3 = null; if ((GUILayout.Button("+ New", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(60f) }) || flag) && _newGroup.Trim().Length > 0) { text3 = Groups.Create(_newGroup); _newGroup = ""; GUIUtility.keyboardControl = 0; } GUILayout.EndHorizontal(); if (Groups.HasPublicGroups()) { GUILayout.Label("<color=#999999>Public groups are already shared with everyone. To share them one by one, make them private under Manage groups (G at nothing).</color>", _hint, Array.Empty<GUILayoutOption>()); } GUILayout.Label("<color=#999999>" + _playerName + " needs this mod to use them.</color>", _hint, Array.Empty<GUILayoutOption>()); if (text != null) { bool flag2 = !Groups.IsSharedWith(text, playerID); Sharing.SetShared(_player, text, flag2); Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)2, flag2 ? ("Shared " + Groups.Name(text) + " with " + _playerName) : ("Stopped sharing " + Groups.Name(text) + " with " + _playerName), 0, (Sprite)null, false); } } if (text3 != null && Groups.IsMine(text3) && !Groups.IsSharedWith(text3, playerID)) { Sharing.SetShared(_player, text3, on: true); Player localPlayer2 = Player.m_localPlayer; if (localPlayer2 != null) { ((Character)localPlayer2).Message((MessageType)2, "Shared " + Groups.Name(text3) + " with " + _playerName, 0, (Sprite)null, false); } } } private static void DrawKeepFilled() { //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Invalid comparison between Unknown and I4 //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Invalid comparison between Unknown and I4 if (!Machines.CanKeepFilled(_target)) { return; } bool flag = ((Time.time - _autoOptimisticTime < 3f) ? _autoOptimistic : Groups.AutoFill(_target)); bool flag2 = GUILayout.Toggle(flag, " Keep filled from chests", Array.Empty<GUILayoutOption>()); if (flag2 != flag) { if (Groups.SetAutoFill(_target, flag2)) { _autoOptimisticTime = -10f; } else { _autoOptimistic = flag2; _autoOptimisticTime = Time.time; } } int num = ((Time.time - _orderOptimisticTime < 3f) ? _orderOptimistic : Machines.OrderLeft(_target)); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); if (num > 0) { GUILayout.Label($"Ordered: {num} more to pull", _hint, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); if (GUILayout.Button("Cancel", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(60f) })) { SetOrder(0); } } else { GUILayout.Label("Pull amount:", _hint, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) }); GUI.SetNextControlName("cfcg_order"); _orderText = GUILayout.TextField(_orderText, 4, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(50f) }); _orderText = new string(_orderText.Where((char ch) => char.IsDigit(ch)).ToArray()); bool flag3 = (int)Event.current.type == 4 && (int)Event.current.keyCode == 13 && GUI.GetNameOfFocusedControl() == "cfcg_order"; if ((GUILayout.Button("Pull", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(50f) }) || flag3) && int.TryParse(_orderText, out var result) && result > 0) { SetOrder(Mathf.Min(result, 9999)); _orderText = ""; GUIUtility.keyboardControl = 0; } } GUILayout.EndHorizontal(); if (Groups.Of(_target).Count == 0) { GUILayout.Label("<color=#999999>Put it in a group to pull from chests.</color>", _hint, Array.Empty<GUILayoutOption>()); } } private static void SetOrder(int amount) { Machines.SetOrder(_target, amount); _orderOptimistic = amount; _orderOptimisticTime = Time.time; } private static void DrawLeaveOne() { if ((Object)(object)((Component)_target).GetComponent<Container>() == (Object)null) { return; } bool flag = ((Time.time - _keepOneOptimisticTime < 3f) ? _keepOneOptimistic : (Groups.GetInt(_target, "cfcg_keep1") != 0)); bool flag2 = GUILayout.Toggle(flag, " Leave 1 of each item in this chest", Array.Empty<GUILayoutOption>()); if (flag2 != flag) { if (Groups.SetInt(_target, "cfcg_keep1", flag2 ? 1 : 0)) { _keepOneOptimisticTime = -10f; } else { _keepOneOptimistic = flag2; _keepOneOptimisticTime = Time.time; } Chests.InvalidateCache(); } } private static void DrawDropFood() { if ((Object)(object)((Component)_target).GetComponentInChildren<CookingStation>(true) == (Object)null) { return; } bool flag = ((Time.time - _dropOptimisticTime < 3f) ? _dropOptimistic : (Groups.GetInt(_target, "cfcg_dropfood") != 0)); bool flag2 = GUILayout.Toggle(flag, " Drop food when done", Array.Empty<GUILayoutOption>()); if (flag2 != flag) { bool flag3 = Groups.SetInt(_target, "cfcg_dropfood", flag2 ? 1 : 0); if (flag3) { _dropOptimisticTime = -10f; } else { _dropOptimistic = flag2; _dropOptimisticTime = Time.time; } if (Plugin.LogTransfers.Value) { Plugin.Log.LogInfo((object)("[Machine] " + Util.At(_targetComp) + ": Drop food when done " + (flag2 ? "on" : "off") + (flag3 ? "" : (" (asked " + Chests.PlayerName(_target.GetZDO().GetOwner()) + "'s game to save it)")))); } } } private static Color Brighten(Color c) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) float num = default(float); float num2 = default(float); float num3 = default(float); Color.RGBToHSV(c, ref num, ref num2, ref num3); return Color.HSVToRGB(num, Mathf.Min(1f, num2 + 0.1f), 0.95f); } private static bool IsIn(HashSet<string> current, string g) { if (Optimistic.TryGetValue(g, out var value) && Time.time - value.Value < 3f) { return value.Key; } return current.Contains(g); } private static void SetMembership(string group, bool on) { Player localPlayer = Player.m_localPlayer; if (!Groups.CanEdit(group, _targetComp)) { if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)2, "Only on things you built", 0, (Sprite)null, false); } return; } Container component = ((Component)_target).GetComponent<Container>(); if ((Object)(object)component != (Object)null && component.IsInUse()) { if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)2, "Someone has that chest open", 0, (Sprite)null, false); } return; } List<string> list = new List<string>(); foreach (string item in Groups.Known()) { if (item.Equals(group, StringComparison.OrdinalIgnoreCase) ? on : IsIn(Groups.Of(_target), item)) { list.Add(item); } } if (Groups.SetAll(_target, list)) { Optimistic.Clear(); } else { Optimistic[group] = new KeyValuePair<bool, float>(on, Time.time); } Chests.InvalidateCache(); Visuals.ForceRefresh(); } } internal static class Groups { private sealed class Waiting { public ZDOID Id; public string Key; public string Str; public int Num; public string BeforeStr; public int BeforeNum; public float Sent; public int Resent; } private sealed class SharedGroup { public string Name; public long OwnerId; public string OwnerName; } public const string ZdoKey = "cfcg_groups"; public const string SetGroupRpc = "cfcg_SetGroup"; public const string AutoFillKey = "cfcg_autofill"; public const string SetAutoFillRpc = "cfcg_SetAutoFill"; public const string Keep1Key = "cfcg_keep1"; public const string OrderKey = "cfcg_order"; public const string DropFoodKey = "cfcg_dropfood"; public const string PlanBuildKey = "cfcg_planbuild"; public const string SetIntRpc = "cfcg_SetInt"; private static readonly HashSet<string> IntKeys = new HashSet<string> { "cfcg_keep1", "cfcg_order", "cfcg_dropfood", "cfcg_planbuild" }; private static readonly StringComparer Cmp = StringComparer.OrdinalIgnoreCase; private static readonly Dictionary<string, HashSet<string>> ParseCache = new Dictionary<string, HashSet<string>>(); private static readonly HashSet<string> EmptySet = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, Waiting> WaitingWrites = new Dictionary<string, Waiting>(); private static float _nextWaitCheck; private const int MaxPending = 50; private static string _opsRaw; private static List<KeyValuePair<string, string>> _ops = new List<KeyValuePair<string, string>>(); private static float _nextSweep; public const char PrivateMark = '~'; private static string _mineRaw; private static string _sharedRaw; private static string _sharesRaw; private static Dictionary<string, string> _mine = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); private static Dictionary<string, SharedGroup> _shared = new Dictionary<string, SharedGroup>(StringComparer.OrdinalIgnoreCase); private static Dictionary<string, HashSet<long>> _shares = new Dictionary<string, HashSet<long>>(StringComparer.OrdinalIgnoreCase); public static string Raw(ZNetView nview) { if ((Object)(object)nview == (Object)null || !nview.IsValid()) { return ""; } ZDO zDO = nview.GetZDO(); if (TryWaiting(zDO, "cfcg_groups", out var w)) { return w.Str; } return RawZdo(zDO); } private static string RawZdo(ZDO zdo) { return zdo.GetString("cfcg_groups", "") ?? ""; } public static H