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 QuickStash v1.4.0
QuickStash.dll
Decompiled a week ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using QuickStash.Config; using QuickStash.Core; using QuickStash.Features; using QuickStash.UI; using TMPro; using UnityEngine; using UnityEngine.Events; 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: IgnoresAccessChecksTo("assembly_guiutils")] [assembly: IgnoresAccessChecksTo("assembly_utils")] [assembly: IgnoresAccessChecksTo("assembly_valheim")] [assembly: AssemblyCompany("valheimcrew")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Guardado rapido en cofres cercanos, favoritos y crafteo desde cofres para Valheim.")] [assembly: AssemblyFileVersion("1.4.0.0")] [assembly: AssemblyInformationalVersion("1.4.0+3d63666ad94932f7c278bffb56aad8ffbb2f7bfd")] [assembly: AssemblyProduct("QuickStash")] [assembly: AssemblyTitle("QuickStash")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.4.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] internal sealed class IsReadOnlyAttribute : Attribute { } [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 QuickStash { [BepInPlugin("com.valheimcrew.quickstash", "QuickStash", "1.4.0")] public class Plugin : BaseUnityPlugin { public const string PluginGuid = "com.valheimcrew.quickstash"; public const string PluginName = "QuickStash"; public const string PluginVersion = "1.4.0"; internal static ManualLogSource Log; private Harmony _harmony; private void Awake() { //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; PluginConfig.Bind(((BaseUnityPlugin)this).Config); PluginConfig.Enabled.SettingChanged += delegate { StashButton.ApplyConfig(); }; PluginConfig.ShowButton.SettingChanged += delegate { StashButton.ApplyConfig(); }; PluginConfig.ButtonLabel.SettingChanged += delegate { StashButton.ApplyConfig(); }; PluginConfig.ButtonOffsetX.SettingChanged += delegate { StashButton.ApplyConfig(); }; PluginConfig.ButtonOffsetY.SettingChanged += delegate { StashButton.ApplyConfig(); }; PluginConfig.PullButtonLabel.SettingChanged += delegate { PullButton.ApplyConfig(); }; PluginConfig.PullButtonOffsetX.SettingChanged += delegate { PullButton.ApplyConfig(); }; PluginConfig.PullButtonOffsetY.SettingChanged += delegate { PullButton.ApplyConfig(); }; _harmony = new Harmony("com.valheimcrew.quickstash"); ApplyPatches(); WarnAboutConflicts(); Log.LogInfo((object)"QuickStash 1.4.0 cargado."); } private void ApplyPatches() { PatchGroup("registro de cofres", typeof(Container_Awake_Patch), typeof(Container_OnDestroyed_Patch)); PatchGroup("ciclo de vida", typeof(Player_OnSpawned_Patch), typeof(Game_Logout_Patch)); PatchGroup("guardado rapido", typeof(Container_RPC_StackResponse_Patch), typeof(InventoryGui_Awake_Patch)); PatchGroup("favoritos", typeof(InventoryGrid_OnLeftDown_Patch), typeof(InventoryGrid_UpdateGui_Patch)); PatchGroup("crafteo desde cofres", typeof(Player_HaveRequirementItems_Patch), typeof(InventoryGui_SetupRequirement_Patch), typeof(InventoryGui_DoCrafting_Patch)); PatchGroup("boton de traer material", typeof(InventoryGui_Awake_PullButton_Patch), typeof(InventoryGui_UpdateRecipe_Patch)); PatchGroup("construccion desde cofres", typeof(Player_HaveRequirements_Piece_Patch), typeof(Player_TryPlacePiece_Patch)); PatchGroup("hornos automaticos", typeof(Smelter_UpdateSmelter_Patch)); } private void PatchGroup(string feature, params Type[] patchTypes) { foreach (Type type in patchTypes) { try { _harmony.PatchAll(type); } catch (Exception ex) { Log.LogError((object)("No se pudo enganchar '" + feature + "' (" + type.Name + "). Esa parte del mod queda desactivada; probablemente el juego cambio en un parche. Detalle: " + ex.Message)); } } } private void Update() { //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) if (PluginConfig.Enabled.Value && !((Object)(object)Player.m_localPlayer == (Object)null)) { FavoriteStore.Tick(); StashService.Tick(); SafeTick(AnimalFeeder.Tick, "alimentacion de animales"); SafeTick(AnimalDiagnostics.Tick, "diagnostico de animales"); KeyboardShortcut value = PluginConfig.Hotkey.Value; if (((KeyboardShortcut)(ref value)).IsDown() && !IsTyping()) { StashService.Run(); } } } private static void SafeTick(Action tick, string what) { try { tick(); } catch (Exception arg) { Log.LogError((object)$"Fallo en {what}: {arg}"); } } private static bool IsTyping() { if ((Object)(object)Chat.instance != (Object)null && Chat.instance.HasFocus()) { return true; } if (!Console.IsVisible() && !TextInput.IsVisible()) { return StoreGui.IsVisible(); } return true; } private void OnDestroy() { FavoriteStore.FlushIfDirty(); Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } private static void WarnAboutConflicts() { string[] array = Chainloader.PluginInfos.Keys.Where((string guid) => guid.IndexOf("craftfromcontainers", StringComparison.OrdinalIgnoreCase) >= 0 || guid.IndexOf("quick_stack", StringComparison.OrdinalIgnoreCase) >= 0 || guid.IndexOf("quickstack", StringComparison.OrdinalIgnoreCase) >= 0).ToArray(); if (array.Length != 0) { Log.LogWarning((object)("Hay otros mods cargados que cubren lo mismo que QuickStash: " + string.Join(", ", array) + ". Conviene dejar solo uno para evitar comportamientos duplicados.")); } } } } namespace QuickStash.UI { [HarmonyPatch(typeof(InventoryGrid), "OnLeftDown")] internal static class InventoryGrid_OnLeftDown_Patch { private static bool Prefix(InventoryGrid __instance, UIInputHandler clickHandler) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) if (!PluginConfig.Enabled.Value || (Object)(object)clickHandler == (Object)null) { return true; } if (!ZInput.GetKey(PluginConfig.FavoriteModifier.Value, true)) { return true; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || __instance.m_inventory != ((Humanoid)localPlayer).GetInventory()) { return true; } try { Vector2i buttonPos = __instance.GetButtonPos(((Component)clickHandler).gameObject); if (buttonPos.x < 0 || buttonPos.y < 0) { return true; } ItemData itemAt = __instance.m_inventory.GetItemAt(buttonPos.x, buttonPos.y); if (itemAt == null || ZInput.GetKey(PluginConfig.SlotModifier.Value, true)) { bool flag = FavoriteStore.ToggleSlot(buttonPos.x, buttonPos.y); ((Character)localPlayer).Message((MessageType)1, flag ? "Casilla protegida" : "Casilla desprotegida", 0, (Sprite)null, false); } else { bool flag2 = FavoriteStore.ToggleItem(itemAt); string text = Localization.instance.Localize(itemAt.m_shared.m_name); ((Character)localPlayer).Message((MessageType)1, (flag2 ? "Favorito: " : "Ya no es favorito: ") + text, 0, (Sprite)null, false); } return false; } catch (Exception arg) { Plugin.Log.LogError((object)$"Fallo al marcar favorito: {arg}"); return true; } } } internal static class FavoriteOverlay { private const string OverlayName = "QuickStashFavorite"; private const int SpriteSize = 32; private const int BorderThickness = 3; private static Sprite _border; private static bool _disabled; private static readonly Dictionary<InventoryElement, Image> Overlays = new Dictionary<InventoryElement, Image>(); private static readonly HashSet<int> FavoritePositions = new HashSet<int>(); private static int Pack(int x, int y) { return (y << 8) | (x & 0xFF); } public static void Refresh(InventoryGrid grid) { //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: 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_00da: 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_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) if (_disabled || !PluginConfig.Enabled.Value || (Object)(object)grid == (Object)null || grid.m_elements == null) { return; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || grid.m_inventory != ((Humanoid)localPlayer).GetInventory()) { return; } FavoritePositions.Clear(); List<ItemData> allItems = grid.m_inventory.GetAllItems(); for (int i = 0; i < allItems.Count; i++) { ItemData val = allItems[i]; if (FavoriteStore.IsFavoriteItem(val)) { FavoritePositions.Add(Pack(val.m_gridPos.x, val.m_gridPos.y)); } } List<InventoryElement> elements = grid.m_elements; for (int j = 0; j < elements.Count; j++) { InventoryElement val2 = elements[j]; if ((Object)(object)val2 == (Object)null) { continue; } Vector2i position = val2.Position; bool flag = FavoriteStore.IsFavoriteSlot(position.x, position.y); bool flag2 = FavoritePositions.Contains(Pack(position.x, position.y)); if (!flag && !flag2) { if (Overlays.TryGetValue(val2, out var value) && (Object)(object)value != (Object)null && ((Behaviour)value).enabled) { ((Behaviour)value).enabled = false; } continue; } Image orCreate = GetOrCreate(val2); if (!((Object)(object)orCreate == (Object)null)) { ((Graphic)orCreate).color = (flag ? PluginConfig.SlotColor : PluginConfig.FavColor); ((Behaviour)orCreate).enabled = true; } } } private static Image GetOrCreate(InventoryElement element) { //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_007d: 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_0088: 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_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) Image value; bool flag = Overlays.TryGetValue(element, out value); if (flag && (Object)(object)value != (Object)null) { return value; } if (flag) { Overlays.Remove(element); PruneDestroyed(); } GameObject val = new GameObject("QuickStashFavorite", new Type[2] { typeof(RectTransform), typeof(Image) }); RectTransform val2 = (RectTransform)val.transform; ((Transform)val2).SetParent(((Component)element).transform, false); val2.anchorMin = Vector2.zero; val2.anchorMax = Vector2.one; val2.offsetMin = Vector2.zero; val2.offsetMax = Vector2.zero; ((Transform)val2).SetAsLastSibling(); Image component = val.GetComponent<Image>(); component.sprite = BorderSprite(); component.type = (Type)1; ((Graphic)component).raycastTarget = false; Overlays[element] = component; return component; } public static void DisableAfterFailure(Exception e) { if (!_disabled) { _disabled = true; Plugin.Log.LogError((object)("Se desactivaron los bordes de favoritos porque el dibujado fallo. " + $"El resto del mod sigue funcionando. Detalle: {e}")); } } private static void PruneDestroyed() { if (Overlays.Count < 128) { return; } List<InventoryElement> list = new List<InventoryElement>(); foreach (KeyValuePair<InventoryElement, Image> overlay in Overlays) { if ((Object)(object)overlay.Key == (Object)null) { list.Add(overlay.Key); } } foreach (InventoryElement item in list) { Overlays.Remove(item); } } private static Sprite BorderSprite() { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Expected O, but got Unknown //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: 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) if ((Object)(object)_border != (Object)null) { return _border; } Texture2D val = new Texture2D(32, 32, (TextureFormat)4, false) { filterMode = (FilterMode)0, wrapMode = (TextureWrapMode)1, hideFlags = (HideFlags)61 }; Color32 val2 = default(Color32); ((Color32)(ref val2))..ctor(byte.MaxValue, byte.MaxValue, byte.MaxValue, (byte)0); Color32 val3 = default(Color32); ((Color32)(ref val3))..ctor(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue); Color32[] array = (Color32[])(object)new Color32[1024]; for (int i = 0; i < 32; i++) { for (int j = 0; j < 32; j++) { bool flag = j < 3 || i < 3 || j >= 29 || i >= 29; array[i * 32 + j] = (flag ? val3 : val2); } } val.SetPixels32(array); val.Apply(false, true); _border = Sprite.Create(val, new Rect(0f, 0f, 32f, 32f), new Vector2(0.5f, 0.5f), 100f, 0u, (SpriteMeshType)0, new Vector4(3f, 3f, 3f, 3f)); ((Object)_border).hideFlags = (HideFlags)61; return _border; } } [HarmonyPatch(typeof(InventoryGrid), "UpdateGui")] internal static class InventoryGrid_UpdateGui_Patch { private static void Postfix(InventoryGrid __instance) { try { FavoriteOverlay.Refresh(__instance); } catch (Exception e) { FavoriteOverlay.DisableAfterFailure(e); } } } internal static class PullButton { [CompilerGenerated] private static class <>O { public static UnityAction <0>__OnClick; } private const float CooldownSeconds = 0.3f; private const float CheckIntervalSeconds = 0.2f; private const int MaxItemsInMessage = 4; private static readonly Dictionary<string, int> Moved = new Dictionary<string, int>(8); private static readonly StringBuilder Summary = new StringBuilder(96); private static GameObject _root; private static RectTransform _rect; private static RectTransform _craftRect; private static TMP_Text _label; private static Button _button; private static Recipe _recipe; private static int _quality; private static int _multiplier; private static bool _hasSomething; private static float _nextCheckAt; private static float _nextAllowedAt; public static void Build(InventoryGui gui) { //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Expected O, but got Unknown //IL_01b4: Unknown result type (might be due to invalid IL or missing references) //IL_01c4: Unknown result type (might be due to invalid IL or missing references) //IL_01d4: Unknown result type (might be due to invalid IL or missing references) //IL_01e4: Unknown result type (might be due to invalid IL or missing references) try { _root = null; _rect = null; _craftRect = null; _label = null; _button = null; _recipe = null; _hasSomething = false; if ((Object)(object)gui == (Object)null || (Object)(object)gui.m_craftButton == (Object)null) { Plugin.Log.LogWarning((object)"No se encontro el boton vanilla de craftear, no se puede crear el boton de traer material. El resto del crafteo desde cofres sigue funcionando."); return; } RectTransform component = ((Component)gui.m_craftButton).GetComponent<RectTransform>(); if ((Object)(object)component == (Object)null) { return; } GameObject val = Object.Instantiate<GameObject>(((Component)gui.m_craftButton).gameObject, ((Transform)component).parent); ((Object)val).name = "QuickStashPullButton"; Localize[] componentsInChildren = val.GetComponentsInChildren<Localize>(true); for (int i = 0; i < componentsInChildren.Length; i++) { Object.Destroy((Object)(object)componentsInChildren[i]); } UIGamePad[] componentsInChildren2 = val.GetComponentsInChildren<UIGamePad>(true); foreach (UIGamePad obj in componentsInChildren2) { obj.m_keyCode = (KeyCode)0; obj.m_zinputKey = string.Empty; } _button = val.GetComponent<Button>(); if ((Object)(object)_button != (Object)null) { ((UnityEventBase)_button.onClick).RemoveAllListeners(); ButtonClickedEvent onClick = _button.onClick; object obj2 = <>O.<0>__OnClick; if (obj2 == null) { UnityAction val2 = OnClick; <>O.<0>__OnClick = val2; obj2 = (object)val2; } ((UnityEvent)onClick).AddListener((UnityAction)obj2); ((Selectable)_button).interactable = true; } UITooltip component2 = val.GetComponent<UITooltip>(); if ((Object)(object)component2 != (Object)null) { component2.m_text = "Trae de los cofres cercanos el material de esta receta, aunque no alcance"; } LayoutElement val3 = val.GetComponent<LayoutElement>(); if ((Object)(object)val3 == (Object)null) { val3 = val.AddComponent<LayoutElement>(); } val3.ignoreLayout = true; _root = val; _rect = val.GetComponent<RectTransform>(); _craftRect = component; _label = val.GetComponentInChildren<TMP_Text>(true); if ((Object)(object)_rect != (Object)null) { _rect.anchorMin = component.anchorMin; _rect.anchorMax = component.anchorMax; _rect.pivot = component.pivot; _rect.sizeDelta = component.sizeDelta; } val.SetActive(false); ApplyConfig(); } catch (Exception arg) { Plugin.Log.LogError((object)$"No se pudo crear el boton de traer material: {arg}"); _root = null; } } public static void ApplyConfig() { if (!((Object)(object)_root == (Object)null)) { if ((Object)(object)_label != (Object)null) { _label.text = PluginConfig.PullButtonLabel.Value; } Reposition(); } } private static void Reposition() { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_008e: 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 (!((Object)(object)_rect == (Object)null) && !((Object)(object)_craftRect == (Object)null)) { Rect rect = _craftRect.rect; float num = ((Rect)(ref rect)).height; if (num < 1f) { num = _craftRect.sizeDelta.y; } if (num < 1f) { num = 40f; } _rect.anchoredPosition = _craftRect.anchoredPosition + new Vector2(0f, 0f - (num + 4f)) + new Vector2(PluginConfig.PullButtonOffsetX.Value, PluginConfig.PullButtonOffsetY.Value); } } public static void Refresh(InventoryGui gui, Player player) { if ((Object)(object)_root == (Object)null) { return; } if (!PluginConfig.Enabled.Value || !PluginConfig.ShowPullButton.Value || !PluginConfig.CraftFromContainers.Value || (Object)(object)gui == (Object)null || (Object)(object)player == (Object)null) { Hide(); return; } if (gui.m_craftTimer >= 0f) { Hide(); return; } Recipe recipe = ((RecipeDataPair)(ref gui.m_selectedRecipe)).Recipe; CraftingStation currentCraftingStation = player.GetCurrentCraftingStation(); if ((Object)(object)recipe == (Object)null || (Object)(object)recipe.m_item == (Object)null || (Object)(object)currentCraftingStation == (Object)null) { Hide(); return; } ItemData itemData = ((RecipeDataPair)(ref gui.m_selectedRecipe)).ItemData; int num = ((itemData == null) ? 1 : (itemData.m_quality + 1)); if (num > recipe.m_item.m_itemData.m_shared.m_maxQuality && !currentCraftingStation.m_upgrader) { Hide(); return; } int num2 = Multiplier(gui, itemData); float unscaledTime = Time.unscaledTime; if ((Object)(object)recipe != (Object)(object)_recipe || num != _quality || num2 != _multiplier) { _recipe = recipe; _quality = num; _multiplier = num2; _nextCheckAt = 0f; } if (unscaledTime >= _nextCheckAt) { _nextCheckAt = unscaledTime + 0.2f; _hasSomething = CraftFromContainers.PullableFromContainers(player, recipe.m_resources, num, num2, recipe.m_requireOnlyOneIngredient) > 0; } if (_root.activeSelf != _hasSomething) { if (_hasSomething) { Reposition(); } _root.SetActive(_hasSomething); } } private static void Hide() { _recipe = null; _hasSomething = false; if ((Object)(object)_root != (Object)null && _root.activeSelf) { _root.SetActive(false); } } private static int Multiplier(InventoryGui gui, ItemData upgrade) { if (upgrade != null) { return 1; } if (!gui.m_touchMultiCrafting && !ZInput.GetButton("AltPlace") && !ZInput.GetButton("JoyLStick")) { return 1; } return Mathf.Max(1, gui.m_multiCraftAmount); } private static void OnClick() { try { Player localPlayer = Player.m_localPlayer; Recipe recipe = _recipe; if (!((Object)(object)localPlayer == (Object)null) && !((Object)(object)recipe == (Object)null) && !((Character)localPlayer).IsTeleporting() && CraftFromContainers.Active(localPlayer)) { float realtimeSinceStartup = Time.realtimeSinceStartup; if (!(realtimeSinceStartup < _nextAllowedAt)) { _nextAllowedAt = realtimeSinceStartup + 0.3f; Moved.Clear(); bool inventoryFull; int total = CraftFromContainers.PullForRecipe(localPlayer, recipe.m_resources, _quality, _multiplier, recipe.m_requireOnlyOneIngredient, Moved, out inventoryFull); _nextCheckAt = 0f; Report(localPlayer, total, inventoryFull); } } } catch (Exception arg) { Plugin.Log.LogError((object)$"Fallo al traer material de los cofres: {arg}"); } } private static void Report(Player player, int total, bool inventoryFull) { //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) if (total <= 0) { ((Character)player).Message((MessageType)2, inventoryFull ? "Sin espacio en el inventario" : "No hay material en los cofres cercanos", 0, (Sprite)null, false); return; } Summary.Length = 0; Summary.Append("Traido: "); int num = 0; foreach (KeyValuePair<string, int> item in Moved) { if (num >= 4) { Summary.Append(", ..."); break; } if (num > 0) { Summary.Append(", "); } Summary.Append(MoveLog.Localize(item.Key)).Append(" x").Append(item.Value); num++; } if (inventoryFull) { Summary.Append(" (inventario lleno)"); } ((Character)player).Message((MessageType)2, Summary.ToString(), 0, (Sprite)null, false); Summary.Length = 0; if ((Object)(object)InventoryGui.instance != (Object)null) { InventoryGui.instance.m_moveItemEffects.Create(((Component)player).transform.position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID)); } } } [HarmonyPatch(typeof(InventoryGui), "Awake")] internal static class InventoryGui_Awake_PullButton_Patch { private static void Postfix(InventoryGui __instance) { PullButton.Build(__instance); } } [HarmonyPatch(typeof(InventoryGui), "UpdateRecipe")] internal static class InventoryGui_UpdateRecipe_Patch { private static void Postfix(InventoryGui __instance, Player player) { try { PullButton.Refresh(__instance, player); } catch (Exception arg) { Plugin.Log.LogError((object)$"Fallo al actualizar el boton de traer material: {arg}"); } } } internal static class StashButton { [CompilerGenerated] private static class <>O { public static UnityAction <0>__Run; } private static GameObject _root; private static RectTransform _rect; private static TMP_Text _label; public static void Build(InventoryGui gui) { //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Expected O, but got Unknown try { if ((Object)(object)gui == (Object)null || (Object)(object)gui.m_player == (Object)null) { return; } if ((Object)(object)gui.m_stackAllButton == (Object)null) { Plugin.Log.LogWarning((object)"No se encontro el boton vanilla de apilar, no se puede crear el boton de guardado. Usa el atajo de teclado configurable en su lugar."); return; } GameObject val = Object.Instantiate<GameObject>(((Component)gui.m_stackAllButton).gameObject, (Transform)(object)gui.m_player); ((Object)val).name = "QuickStashButton"; Localize[] componentsInChildren = val.GetComponentsInChildren<Localize>(true); for (int i = 0; i < componentsInChildren.Length; i++) { Object.Destroy((Object)(object)componentsInChildren[i]); } Button component = val.GetComponent<Button>(); if ((Object)(object)component != (Object)null) { ((UnityEventBase)component.onClick).RemoveAllListeners(); ButtonClickedEvent onClick = component.onClick; object obj = <>O.<0>__Run; if (obj == null) { UnityAction val2 = StashService.Run; <>O.<0>__Run = val2; obj = (object)val2; } ((UnityEvent)onClick).AddListener((UnityAction)obj); } _root = val; _rect = val.GetComponent<RectTransform>(); _label = val.GetComponentInChildren<TMP_Text>(true); if ((Object)(object)_rect != (Object)null) { _rect.anchorMin = new Vector2(1f, 1f); _rect.anchorMax = new Vector2(1f, 1f); _rect.pivot = new Vector2(1f, 1f); } ApplyConfig(); } catch (Exception arg) { Plugin.Log.LogError((object)$"No se pudo crear el boton de guardado: {arg}"); } } public static void ApplyConfig() { //IL_0084: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_root == (Object)null)) { _root.SetActive(PluginConfig.Enabled.Value && PluginConfig.ShowButton.Value); if ((Object)(object)_label != (Object)null) { _label.text = PluginConfig.ButtonLabel.Value; } if ((Object)(object)_rect != (Object)null) { _rect.anchoredPosition = new Vector2(-12f + PluginConfig.ButtonOffsetX.Value, 36f + PluginConfig.ButtonOffsetY.Value); } } } } [HarmonyPatch(typeof(InventoryGui), "Awake")] internal static class InventoryGui_Awake_Patch { private static void Postfix(InventoryGui __instance) { StashButton.Build(__instance); } } } namespace QuickStash.Features { internal static class AnimalFeeder { private const float ScanRange = 40f; private const float MinFeedIntervalSeconds = 10f; private const float ClosestEpsilon = 1.5f; private static readonly Collider[] ItemHits = (Collider[])(object)new Collider[256]; private static readonly List<Container> Buffer = new List<Container>(16); private static readonly List<ItemData> ItemBuffer = new List<ItemData>(32); private static readonly Dictionary<MonsterAI, float> NextFeed = new Dictionary<MonsterAI, float>(); private static readonly List<MonsterAI> PruneBuffer = new List<MonsterAI>(8); private static readonly HashSet<string> LoggedMenus = new HashSet<string>(StringComparer.Ordinal); private static readonly StringBuilder MenuBuilder = new StringBuilder(128); private static float _nextRun; private static int _itemMask; public static void Tick() { //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: 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_00f7: 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) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) if (!PluginConfig.Enabled.Value || !PluginConfig.FeedAnimals.Value) { return; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return; } float realtimeSinceStartup = Time.realtimeSinceStartup; if (realtimeSinceStartup < _nextRun) { return; } _nextRun = realtimeSinceStartup + PluginConfig.AnimalIntervalSeconds.Value; List<BaseAI> instances = BaseAI.m_instances; if (instances == null) { return; } PruneStale(realtimeSinceStartup); Vector3 position = ((Component)localPlayer).transform.position; float num = 1600f; long playerId = ContainerAccess.LocalPlayerId(); int num2 = PluginConfig.AnimalMaxPerCycle.Value; for (int i = 0; i < instances.Count; i++) { if (num2 <= 0) { break; } BaseAI obj = instances[i]; MonsterAI val = (MonsterAI)(object)((obj is MonsterAI) ? obj : null); if ((Object)(object)val == (Object)null) { continue; } Tameable tamable = ((BaseAI)val).m_tamable; if ((Object)(object)tamable == (Object)null || !tamable.IsTamed()) { continue; } Character character = ((BaseAI)val).m_character; if (!((Object)(object)character == (Object)null) && !character.IsDead()) { Vector3 position2 = ((Component)val).transform.position; Vector3 val2 = position2 - position; if (!(((Vector3)(ref val2)).sqrMagnitude > num) && (!NextFeed.TryGetValue(val, out var value) || !(realtimeSinceStartup < value)) && tamable.IsHungry() && val.m_consumeItems != null && val.m_consumeItems.Count != 0 && IsClosestPlayer(localPlayer, position2) && !HasFoodOnGround(val) && Feed(val, position2, playerId)) { NextFeed[val] = realtimeSinceStartup + Mathf.Max(10f, PluginConfig.AnimalIntervalSeconds.Value); num2--; } } } } private static bool IsClosestPlayer(Player local, Vector3 animalPosition) { //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_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) List<Player> allPlayers = Player.GetAllPlayers(); if (allPlayers == null || allPlayers.Count <= 1) { return true; } float num = Vector3.Distance(((Component)local).transform.position, animalPosition); long playerID = local.GetPlayerID(); foreach (Player item in allPlayers) { if (!((Object)(object)item == (Object)null) && !((Object)(object)item == (Object)(object)local)) { float num2 = Vector3.Distance(((Component)item).transform.position, animalPosition) - num; if (num2 < -1.5f) { return false; } if (Mathf.Abs(num2) <= 1.5f && item.GetPlayerID() < playerID) { return false; } } } return true; } private static bool HasFoodOnGround(MonsterAI ai) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) if (_itemMask == 0) { _itemMask = LayerMask.GetMask(new string[1] { "item" }); } int num = Physics.OverlapSphereNonAlloc(((Component)ai).transform.position, ai.m_consumeSearchRange, ItemHits, _itemMask); if (num >= ItemHits.Length) { return true; } for (int i = 0; i < num; i++) { Collider val = ItemHits[i]; if (!((Object)(object)val == (Object)null) && !((Object)(object)val.attachedRigidbody == (Object)null)) { ItemDrop component = ((Component)val.attachedRigidbody).GetComponent<ItemDrop>(); if (!((Object)(object)component == (Object)null) && component.m_itemData?.m_shared != null && ai.CanConsume(component.m_itemData)) { return true; } } } return false; } private static bool Feed(MonsterAI ai, Vector3 animalPosition, long playerId) { //IL_0000: 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_00e6: 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) ContainerRegistry.Query(animalPosition, PluginConfig.AnimalFeedRange.Value, PluginConfig.MaxScanned.Value, playerId, Buffer); int num = 0; int num2 = 0; foreach (Container item in Buffer) { if ((Object)(object)item.m_nview == (Object)null || !item.m_nview.IsOwner()) { continue; } num++; Inventory inventory = item.GetInventory(); if (inventory == null) { continue; } ItemData val = FindFood(ai, inventory); if (val == null) { continue; } num2++; if (DropOne(ai, item, inventory, val, animalPosition)) { if (PluginConfig.DebugTiming.Value) { Plugin.Log.LogInfo((object)("[animal] " + Name(ai) + ": se le dejo " + MoveLog.Localize(val.m_shared.m_name) + " " + $"de un cofre a {Vector3.Distance(animalPosition, ((Component)item).transform.position):F1} m")); } return true; } } Explain(ai, num, num2); return false; } private static ItemData FindFood(MonsterAI ai, Inventory source) { ItemBuffer.Clear(); ItemBuffer.AddRange(source.GetAllItems()); try { foreach (ItemData item in ItemBuffer) { if (item?.m_shared != null && item.m_stack > 0 && !((Object)(object)item.m_dropPrefab == (Object)null) && !PluginConfig.IsExcluded(item) && !PluginConfig.IsExcludedForAnimals(item) && ai.CanConsume(item)) { return item; } } } finally { ItemBuffer.Clear(); } return null; } private static bool DropOne(MonsterAI ai, Container container, Inventory source, ItemData item, Vector3 animalPosition) { //IL_0026: 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_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: 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_0057: Unknown result type (might be due to invalid IL or missing references) ItemData val = item.Clone(); val.m_stack = 1; string name = item.m_shared.m_name; if (!source.RemoveItem(item, 1)) { return false; } Vector3 val2 = Random.insideUnitSphere * 0.3f; Vector3 val3 = animalPosition + new Vector3(val2.x, 0.4f, val2.z); try { ItemDrop.DropItem(val, 1, val3, Quaternion.identity); } catch (Exception ex) { Plugin.Log.LogError((object)("Se perdio 1 x " + MoveLog.Localize(name) + " al dejarsela a " + Name(ai) + ": " + ex.Message)); return false; } MoveLog.RecordAnimalFeed(container, name, 1); return true; } private static void PruneStale(float now) { if (NextFeed.Count < 64) { return; } PruneBuffer.Clear(); foreach (KeyValuePair<MonsterAI, float> item in NextFeed) { if ((Object)(object)item.Key == (Object)null || now > item.Value + 300f) { PruneBuffer.Add(item.Key); } } foreach (MonsterAI item2 in PruneBuffer) { NextFeed.Remove(item2); } PruneBuffer.Clear(); } private static void Explain(MonsterAI ai, int owned, int withFood) { if (PluginConfig.DebugTiming.Value) { string text = ((Buffer.Count == 0) ? $"no hay ningun cofre accesible a menos de {PluginConfig.AnimalFeedRange.Value:F0} m del animal" : ((owned == 0) ? ($"hay {Buffer.Count} cofre(s) cerca pero ninguno es tuyo " + "(en el servidor la propiedad del ZDO puede estar en otro jugador)") : ((withFood != 0) ? "se encontro comida pero no se pudo sacar del cofre" : $"hay {owned} cofre(s) tuyo(s) cerca, pero ninguno tiene comida que este animal acepte"))); Plugin.Log.LogInfo((object)("[animal] " + Name(ai) + " tiene hambre y no comio: " + text + ".")); LogMenuOnce(ai); } } private static void LogMenuOnce(MonsterAI ai) { string text = Name(ai); if (!LoggedMenus.Add(text)) { return; } MenuBuilder.Length = 0; foreach (ItemDrop consumeItem in ai.m_consumeItems) { if (!((Object)(object)consumeItem == (Object)null) && consumeItem.m_itemData?.m_shared != null) { if (MenuBuilder.Length > 0) { MenuBuilder.Append(", "); } MenuBuilder.Append(MoveLog.Localize(consumeItem.m_itemData.m_shared.m_name)); } } Plugin.Log.LogInfo((object)$"[animal] {text} come: {MenuBuilder}"); MenuBuilder.Length = 0; } private static string Name(MonsterAI ai) { Character character = ((BaseAI)ai).m_character; if (!((Object)(object)character != (Object)null)) { return ((Object)ai).name; } return MoveLog.Localize(character.m_name); } public static void Reset() { Buffer.Clear(); ItemBuffer.Clear(); NextFeed.Clear(); PruneBuffer.Clear(); LoggedMenus.Clear(); _nextRun = 0f; } } internal static class BuildFromContainers { private const int BuildQualityLevel = 0; private const int BuildMultiplier = 1; public static void PullFor(Player player, Piece piece) { CraftFromContainers.PullMissing(player, piece.m_resources, 0, -1, 1, onlyOneIngredient: false); } public static bool CoveredByInventory(Player player, Piece piece) { Inventory inventory = ((Humanoid)player).GetInventory(); Requirement[] resources = piece.m_resources; foreach (Requirement val in resources) { if (Object.op_Implicit((Object)(object)val.m_resItem) && val.m_amount > 0) { string name = val.m_resItem.m_itemData.m_shared.m_name; if (inventory.CountItems(name, -1, true) < val.m_amount) { return false; } } } return true; } public static bool HaveWithContainers(Player player, Piece piece, RequirementMode mode) { //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Invalid comparison between Unknown and I4 Inventory inventory = ((Humanoid)player).GetInventory(); int num = inventory.GetEmptySlots(); Requirement[] resources = piece.m_resources; foreach (Requirement val in resources) { if (!Object.op_Implicit((Object)(object)val.m_resItem) || val.m_amount <= 0) { continue; } string name = val.m_resItem.m_itemData.m_shared.m_name; int num2 = inventory.CountItems(name, -1, true); int num3 = num2; if (num2 > 0 || num > 0) { num3 += CraftFromContainers.InContainers(player, name, -1); if (num2 == 0) { num--; } } int num4 = (((int)mode == 2) ? 1 : val.m_amount); if (num3 < num4) { return false; } } return true; } } [HarmonyPatch(typeof(Player), "HaveRequirements", new Type[] { typeof(Piece), typeof(RequirementMode) })] internal static class Player_HaveRequirements_Piece_Patch { private static void Postfix(Player __instance, Piece piece, RequirementMode mode, ref bool __result) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Invalid comparison between Unknown and I4 //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) if (__result || (Object)(object)piece == (Object)null || (int)mode == 1 || !CraftFromContainers.BuildActive(__instance)) { return; } try { if (HasStationAndDlc(__instance, piece, mode)) { __result = BuildFromContainers.HaveWithContainers(__instance, piece, mode); } } catch (Exception arg) { Plugin.Log.LogError((object)$"Fallo al evaluar requisitos de construccion con cofres: {arg}"); } } private static bool HasStationAndDlc(Player player, Piece piece, RequirementMode mode) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Invalid comparison between Unknown and I4 //IL_0023: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)piece.m_craftingStation != (Object)null) { bool num; if ((int)mode != 2) { if (Object.op_Implicit((Object)(object)CraftingStation.HaveBuildStationInRange(piece.m_craftingStation.m_name, ((Component)player).transform.position))) { goto IL_005f; } num = ZoneSystem.instance.GetGlobalKey((GlobalKeys)27); } else { num = player.m_knownStations.ContainsKey(piece.m_craftingStation.m_name); } if (!num) { return false; } } goto IL_005f; IL_005f: if (piece.m_dlc.Length <= 0) { return true; } if ((Object)(object)DLCMan.instance != (Object)null) { return DLCMan.instance.IsDLCInstalled(piece.m_dlc); } return false; } } [HarmonyPatch(typeof(Player), "TryPlacePiece")] internal static class Player_TryPlacePiece_Patch { private static bool Prefix(Player __instance, Piece piece, ref bool __result) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)piece == (Object)null || !CraftFromContainers.BuildActive(__instance)) { return true; } try { if ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetGlobalKey(piece.FreeBuildKey())) { return true; } if ((int)__instance.m_placementStatus != 0) { return true; } BuildFromContainers.PullFor(__instance, piece); if (!BuildFromContainers.CoveredByInventory(__instance, piece)) { ((Character)__instance).Message((MessageType)2, "$msg_missingrequirement", 0, (Sprite)null, false); __result = false; return false; } } catch (Exception arg) { Plugin.Log.LogError((object)$"Fallo al traer material de los cofres para construir: {arg}"); } return true; } } internal static class CraftFromContainers { public static readonly Color AvailableColor = new Color(0.55f, 0.85f, 1f); public static bool Active(Player player) { if (PluginConfig.Enabled.Value && PluginConfig.CraftFromContainers.Value && (Object)(object)player != (Object)null) { return (Object)(object)player.GetCurrentCraftingStation() != (Object)null; } return false; } public static bool IsBuilding(Player player) { if ((Object)(object)player != (Object)null && ((Character)player).InPlaceMode()) { return !InventoryGui.IsVisible(); } return false; } public static bool BuildActive(Player player) { if (PluginConfig.Enabled.Value && PluginConfig.BuildFromContainers.Value) { return IsBuilding(player); } return false; } private static float ActiveRange(Player player) { if (!IsBuilding(player)) { return PluginConfig.CraftRange.Value; } return PluginConfig.BuildRange.Value; } private static void Refresh(Player player) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) ContainerIndex.EnsureFresh(((Component)player).transform.position, ActiveRange(player), ContainerAccess.LocalPlayerId()); } public static int InContainers(Player player, string name, int quality) { Refresh(player); return ContainerIndex.Count(name, quality); } public static bool CanReceive(Inventory inventory, int alreadyInInventory) { if (alreadyInInventory <= 0) { return inventory.HaveEmptySlot(); } return true; } public static int CountAvailable(Player player, string name, int quality) { Inventory inventory = ((Humanoid)player).GetInventory(); if (!CanReceive(inventory, inventory.CountItems(name, -1, true))) { return 0; } return InContainers(player, name, quality); } public static bool IsRelevant(Requirement requirement, CraftingStation station) { if ((Object)(object)requirement.m_resItem == (Object)null) { return false; } if ((Object)(object)station != (Object)null && station.m_upgrader != requirement.m_upgraderResource) { return false; } if (!((Object)(object)station != (Object)null)) { return !requirement.m_upgraderResource; } return true; } public static bool CoveredInSingleQuality(Inventory inventory, Requirement requirement, int need) { string name = requirement.m_resItem.m_itemData.m_shared.m_name; int maxQuality = requirement.m_resItem.m_itemData.m_shared.m_maxQuality; for (int i = 0; i <= maxQuality; i++) { if (inventory.CountItems(name, i, true) >= need) { return true; } } return false; } public static bool AnyIngredientCovered(Player player, Requirement[] requirements, int qualityLevel, int multiplier) { CraftingStation currentCraftingStation = player.GetCurrentCraftingStation(); Inventory inventory = ((Humanoid)player).GetInventory(); foreach (Requirement val in requirements) { if (IsRelevant(val, currentCraftingStation)) { int num = val.GetAmount(qualityLevel) * multiplier; if (num > 0 && CoveredInSingleQuality(inventory, val, num)) { return true; } } } return false; } public static void PullMissing(Player player, Recipe recipe, int qualityLevel, int itemQuality, int multiplier) { PullMissing(player, recipe.m_resources, qualityLevel, itemQuality, multiplier, recipe.m_requireOnlyOneIngredient); } public static void PullMissing(Player player, Requirement[] requirements, int qualityLevel, int itemQuality, int multiplier, bool onlyOneIngredient) { CraftingStation currentCraftingStation = player.GetCurrentCraftingStation(); Inventory inventory = ((Humanoid)player).GetInventory(); bool flag = false; if (onlyOneIngredient && AnyIngredientCovered(player, requirements, qualityLevel, multiplier)) { return; } foreach (Requirement val in requirements) { if (!IsRelevant(val, currentCraftingStation)) { continue; } int num = val.GetAmount(qualityLevel) * multiplier; if (num <= 0) { continue; } string name = val.m_resItem.m_itemData.m_shared.m_name; int num2 = num - inventory.CountItems(name, itemQuality, true); if (num2 > 0 && (!onlyOneIngredient || CountAvailable(player, name, itemQuality) >= num2) && Pull(player, inventory, name, itemQuality, num2, out var _) > 0) { flag = true; if (onlyOneIngredient) { break; } } } if (flag) { ContainerIndex.Invalidate(); } } public static int PullableFromContainers(Player player, Requirement[] requirements, int qualityLevel, int multiplier, bool onlyOneIngredient) { bool inventoryFull; return Transfer(player, requirements, qualityLevel, multiplier, onlyOneIngredient, dryRun: true, null, out inventoryFull); } public static int PullForRecipe(Player player, Requirement[] requirements, int qualityLevel, int multiplier, bool onlyOneIngredient, Dictionary<string, int> moved, out bool inventoryFull) { return Transfer(player, requirements, qualityLevel, multiplier, onlyOneIngredient, dryRun: false, moved, out inventoryFull); } private static int Transfer(Player player, Requirement[] requirements, int qualityLevel, int multiplier, bool onlyOneIngredient, bool dryRun, Dictionary<string, int> moved, out bool inventoryFull) { inventoryFull = false; if ((Object)(object)player == (Object)null || requirements == null) { return 0; } CraftingStation currentCraftingStation = player.GetCurrentCraftingStation(); Inventory inventory = ((Humanoid)player).GetInventory(); Requirement val = null; if (onlyOneIngredient) { if (AnyIngredientCovered(player, requirements, qualityLevel, multiplier)) { return 0; } val = ChooseSingleIngredient(player, requirements, qualityLevel, multiplier, currentCraftingStation); if (val == null) { return 0; } } int num = 0; foreach (Requirement val2 in requirements) { if ((val != null && val2 != val) || !IsRelevant(val2, currentCraftingStation)) { continue; } int num2 = val2.GetAmount(qualityLevel) * multiplier; if (num2 <= 0) { continue; } string name = val2.m_resItem.m_itemData.m_shared.m_name; int num3 = num2 - inventory.CountItems(name, -1, true); if (num3 <= 0) { continue; } int num4 = CountAvailable(player, name, -1); if (num4 <= 0) { continue; } if (dryRun) { num += Mathf.Min(num3, num4); continue; } bool inventoryFull2; int num5 = Pull(player, inventory, name, -1, num3, out inventoryFull2); if (num5 > 0) { num += num5; if (moved != null) { moved.TryGetValue(name, out var value); moved[name] = value + num5; } } if (inventoryFull2) { inventoryFull = true; break; } } if (!dryRun && num > 0) { ContainerIndex.Invalidate(); } return num; } private static Requirement ChooseSingleIngredient(Player player, Requirement[] requirements, int qualityLevel, int multiplier, CraftingStation station) { Inventory inventory = ((Humanoid)player).GetInventory(); Requirement result = null; int num = 0; foreach (Requirement val in requirements) { if (!IsRelevant(val, station)) { continue; } int num2 = val.GetAmount(qualityLevel) * multiplier; if (num2 > 0) { string name = val.m_resItem.m_itemData.m_shared.m_name; int num3 = Mathf.Min(num2, inventory.CountItems(name, -1, true) + CountAvailable(player, name, -1)); if (num3 > num) { num = num3; result = val; } } } return result; } private static int Pull(Player player, Inventory playerInventory, string name, int itemQuality, int missing, out bool inventoryFull) { Refresh(player); inventoryFull = false; int num = 0; long playerId = ContainerAccess.LocalPlayerId(); IReadOnlyList<Container> containers = ContainerIndex.Containers; for (int i = 0; i < 2; i++) { if (missing <= 0) { break; } bool flag = i == 0; for (int j = 0; j < containers.Count; j++) { if (missing <= 0) { break; } Container val = containers[j]; if (!ContainerAccess.CanUse(val, playerId) || val.m_nview.IsOwner() != flag) { continue; } Inventory inventory = val.GetInventory(); if (inventory != null && inventory.CountItems(name, itemQuality, true) > 0) { val.m_nview.ClaimOwnership(); bool inventoryFull2; int num2 = PullFrom(val, inventory, playerInventory, name, itemQuality, missing, out inventoryFull2); missing -= num2; num += num2; if (inventoryFull2) { inventoryFull = true; return num; } } } } return num; } private static int PullFrom(Container owner, Inventory source, Inventory playerInventory, string name, int itemQuality, int missing, out bool inventoryFull) { inventoryFull = false; int num = 0; Action onChanged = source.m_onChanged; source.m_onChanged = null; try { foreach (ItemData item in new List<ItemData>(source.GetAllItems())) { if (missing <= 0) { break; } if (item?.m_shared != null && !(item.m_shared.m_name != name) && item.m_worldLevel >= Game.m_worldLevel && (itemQuality < 0 || item.m_quality == itemQuality)) { int stack = Mathf.Min(missing, item.m_stack); ItemData val = item.Clone(); val.m_stack = stack; int num2 = playerInventory.CountItems(name, itemQuality, true); bool num3 = playerInventory.AddItem(val); int num4 = playerInventory.CountItems(name, itemQuality, true) - num2; if (num4 > 0) { source.RemoveItem(item, num4); missing -= num4; num += num4; } if (!num3) { inventoryFull = true; break; } } } } finally { source.m_onChanged = onChanged; } if (num > 0) { source.Changed(false, false); MoveLog.RecordSingle(intoContainer: false, owner, name, num); } return num; } } [HarmonyPatch(typeof(Player), "HaveRequirementItems")] internal static class Player_HaveRequirementItems_Patch { private static void Postfix(Player __instance, Recipe piece, bool discover, int qualityLevel, int amount, ref bool __result) { if (__result || discover || (Object)(object)piece == (Object)null || !CraftFromContainers.Active(__instance)) { return; } try { CraftingStation currentCraftingStation = __instance.GetCurrentCraftingStation(); Inventory inventory = ((Humanoid)__instance).GetInventory(); bool requireOnlyOneIngredient = piece.m_requireOnlyOneIngredient; Requirement[] resources = piece.m_resources; foreach (Requirement val in resources) { if (!CraftFromContainers.IsRelevant(val, currentCraftingStation)) { continue; } int num = val.GetAmount(qualityLevel) * amount; string name = val.m_resItem.m_itemData.m_shared.m_name; int num2 = (CraftFromContainers.CanReceive(inventory, inventory.CountItems(name, -1, true)) ? CraftFromContainers.InContainers(__instance, name, -1) : 0); int num3 = 0; for (int j = 1; j < val.m_resItem.m_itemData.m_shared.m_maxQuality + 1; j++) { int num4 = inventory.CountItems(name, j, true); if (num2 > 0) { num4 += CraftFromContainers.InContainers(__instance, name, j); } if (num4 > num3) { num3 = num4; } } if (requireOnlyOneIngredient) { if (num3 >= num) { __result = true; return; } } else if (num3 < num) { return; } } __result = !requireOnlyOneIngredient; } catch (Exception arg) { Plugin.Log.LogError((object)$"Fallo al evaluar requisitos con cofres: {arg}"); } } } [HarmonyPatch(typeof(InventoryGui), "SetupRequirement")] internal static class InventoryGui_SetupRequirement_Patch { private static void Postfix(Transform elementRoot, Requirement req, Player player, bool craft, int quality, int craftMultiplier, bool __result) { //IL_011c: Unknown result type (might be due to invalid IL or missing references) if (!__result || req == null || (Object)(object)req.m_resItem == (Object)null || (Object)(object)player == (Object)null || !PluginConfig.Enabled.Value) { return; } bool value = PluginConfig.ShowAvailable.Value; if (!value && !PluginConfig.ShowContainerTotals.Value) { return; } bool flag = (CraftFromContainers.IsBuilding(player) ? CraftFromContainers.BuildActive(player) : (craft && CraftFromContainers.Active(player))); try { int num = req.GetAmount(quality) * craftMultiplier; if (num <= 0) { return; } string name = req.m_resItem.m_itemData.m_shared.m_name; int num2 = ((Humanoid)player).GetInventory().CountItems(name, -1, true); int num3 = num2; if (flag) { num3 += CraftFromContainers.CountAvailable(player, name, -1); } Transform val = elementRoot.Find("res_amount"); if ((Object)(object)val == (Object)null) { return; } TMP_Text component = ((Component)val).GetComponent<TMP_Text>(); if (!((Object)(object)component == (Object)null)) { if (value) { component.text = num3 + "/" + num; } if (PluginConfig.ShowContainerTotals.Value && flag && num2 < num && num3 >= num) { ((Graphic)component).color = CraftFromContainers.AvailableColor; } } } catch (Exception arg) { Plugin.Log.LogError((object)$"Fallo al pintar el requisito: {arg}"); } } } [HarmonyPatch(typeof(InventoryGui), "DoCrafting")] internal static class InventoryGui_DoCrafting_Patch { private static bool Prefix(InventoryGui __instance, Player player) { if (!CraftFromContainers.Active(player) || (Object)(object)__instance.m_craftRecipe == (Object)null) { return true; } try { Recipe craftRecipe = __instance.m_craftRecipe; int qualityLevel = ((__instance.m_craftUpgradeItem == null) ? 1 : (__instance.m_craftUpgradeItem.m_quality + 1)); int num = ((!__instance.m_multiCrafting) ? 1 : __instance.m_multiCraftAmount); if (__instance.m_craftUpgradeItem == null && !craftRecipe.m_requireOnlyOneIngredient && !((Humanoid)player).GetInventory().CanAddItem(((Component)craftRecipe.m_item).gameObject, craftRecipe.m_amount * num)) { return true; } CraftFromContainers.PullMissing(player, craftRecipe, qualityLevel, -1, num); if (craftRecipe.m_requireOnlyOneIngredient && !CraftFromContainers.AnyIngredientCovered(player, craftRecipe.m_resources, qualityLevel, num)) { ((Character)player).Message((MessageType)2, "$msg_missingrequirement", 0, (Sprite)null, false); return false; } } catch (Exception arg) { Plugin.Log.LogError((object)$"Fallo al traer material de los cofres: {arg}"); } return true; } } [Serializable] internal class FavoriteData { public List<string> items = new List<string>(); public List<string> slots = new List<string>(); } internal static class FavoriteStore { private const float SaveDelaySeconds = 2f; private const float RetryDelaySeconds = 30f; private const int MaxLoggedFailures = 3; private static readonly HashSet<string> FavoriteItems = new HashSet<string>(StringComparer.Ordinal); private static readonly HashSet<int> FavoriteSlots = new HashSet<int>(); private static string _profileKey; private static bool _dirty; private static float _saveAt; private static int _saveFailures; private static string Folder => Path.Combine(Paths.ConfigPath, "QuickStash"); private static string FilePath => Path.Combine(Folder, "favorites." + (_profileKey ?? "default") + ".json"); private static int PackSlot(int x, int y) { return (y << 8) | (x & 0xFF); } public static void LoadForCurrentProfile() { string text = ResolveProfileKey(); if (text == _profileKey) { return; } FlushIfDirty(); _profileKey = text; FavoriteItems.Clear(); FavoriteSlots.Clear(); try { if (File.Exists(FilePath)) { FavoriteData favoriteData = JsonUtility.FromJson<FavoriteData>(File.ReadAllText(FilePath)); if (favoriteData != null) { if (favoriteData.items != null) { foreach (string item in favoriteData.items) { FavoriteItems.Add(item); } } if (favoriteData.slots != null) { foreach (string slot in favoriteData.slots) { string[] array = slot.Split(new char[1] { ',' }); if (array.Length == 2 && int.TryParse(array[0], out var result) && int.TryParse(array[1], out var result2)) { FavoriteSlots.Add(PackSlot(result, result2)); } } } } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("No se pudieron leer los favoritos de '" + _profileKey + "': " + ex.Message)); } _dirty = false; _saveFailures = 0; } private static string ResolveProfileKey() { PlayerProfile val = (((Object)(object)Game.instance != (Object)null) ? Game.instance.GetPlayerProfile() : null); if (val == null) { return null; } string text = val.GetFilename(); if (string.IsNullOrEmpty(text)) { text = val.GetName(); } if (string.IsNullOrEmpty(text)) { return "default"; } char[] invalidFileNameChars = Path.GetInvalidFileNameChars(); foreach (char oldChar in invalidFileNameChars) { text = text.Replace(oldChar, '_'); } return text; } public static bool IsFavoriteItem(ItemData item) { if (item?.m_shared != null) { return FavoriteItems.Contains(item.m_shared.m_name); } return false; } public static bool IsFavoriteSlot(int x, int y) { return FavoriteSlots.Contains(PackSlot(x, y)); } public static bool IsProtected(ItemData item) { if (item == null) { return false; } if (!IsFavoriteItem(item)) { return IsFavoriteSlot(item.m_gridPos.x, item.m_gridPos.y); } return true; } public static bool ToggleItem(ItemData item) { if (item?.m_shared == null) { return false; } string name = item.m_shared.m_name; bool result; if (FavoriteItems.Contains(name)) { FavoriteItems.Remove(name); result = false; } else { FavoriteItems.Add(name); result = true; } MarkDirty(); return result; } public static bool ToggleSlot(int x, int y) { int item = PackSlot(x, y); bool result; if (FavoriteSlots.Contains(item)) { FavoriteSlots.Remove(item); result = false; } else { FavoriteSlots.Add(item); result = true; } MarkDirty(); return result; } private static void MarkDirty() { _dirty = true; _saveAt = Time.realtimeSinceStartup + 2f; } public static void Tick() { if (_dirty && Time.realtimeSinceStartup >= _saveAt) { Save(); } } public static void FlushIfDirty() { if (_dirty) { Save(); } } private static void Save() { if (_profileKey == null) { _dirty = false; return; } try { Directory.CreateDirectory(Folder); FavoriteData favoriteData = new FavoriteData(); favoriteData.items.AddRange(FavoriteItems); foreach (int favoriteSlot in FavoriteSlots) { favoriteData.slots.Add((favoriteSlot & 0xFF) + "," + (favoriteSlot >> 8)); } string text = FilePath + ".tmp"; File.WriteAllText(text, JsonUtility.ToJson((object)favoriteData, true)); if (File.Exists(FilePath)) { File.Delete(FilePath); } File.Move(text, FilePath); _dirty = false; _saveFailures = 0; } catch (Exception ex) { _saveFailures++; _saveAt = Time.realtimeSinceStartup + 30f; if (_saveFailures <= 3) { Plugin.Log.LogWarning((object)("No se pudieron guardar los favoritos de '" + _profileKey + "': " + ex.Message)); } } } } internal static class StashService { private struct Request { public int ActionId; public float ExpiresAt; } private const float ResponseTimeoutSeconds = 5f; private const float RequestGraceSeconds = 60f; private const float CooldownSeconds = 0.5f; private static readonly List<Container> Buffer = new List<Container>(64); private static readonly List<ItemData> ItemBuffer = new List<ItemData>(64); private static readonly Dictionary<Container, Request> Requested = new Dictionary<Container, Request>(); private static readonly List<Container> PruneBuffer = new List<Container>(16); private static readonly Dictionary<string, int> MovedBuffer = new Dictionary<string, int>(16); private static int _actionId; private static int _awaiting; private static int _requested; private static int _matching; private static int _inRange; private static int _usable; private static bool _dispatching; private static bool _reported; private static bool _running; private static float _deadline; private static float _nextAllowedAt; private static int _movedTotal; private static int _containersTouched; private static int _denied; private static float _startedAt; private static float _syncMs; private static ItemData _dragged; public static void Run() { //IL_011d: Unknown result type (might be due to invalid IL or missing references) if (!PluginConfig.Enabled.Value) { return; } float realtimeSinceStartup = Time.realtimeSinceStartup; if (_running || realtimeSinceStartup < _nextAllowedAt) { return; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsTeleporting()) { return; } _nextAllowedAt = realtimeSinceStartup + 0.5f; _actionId++; _movedTotal = 0; _containersTouched = 0; _denied = 0; _awaiting = 0; _requested = 0; _matching = 0; _reported = false; _startedAt = realtimeSinceStartup; _running = true; _deadline = realtimeSinceStartup + 5f; _dragged = (((Object)(object)InventoryGui.instance != (Object)null) ? InventoryGui.instance.m_dragItem : null); _dispatching = true; try { long num = ContainerAccess.LocalPlayerId(); Container val = (((Object)(object)InventoryGui.instance != (Object)null) ? InventoryGui.instance.m_currentContainer : null); if (PluginConfig.IncludeOpenContainer.Value && (Object)(object)val != (Object)null) { int num2 = StackInto(val, localPlayer); if (num2 > 0) { _movedTotal += num2; _containersTouched++; } } ContainerRegistry.Query(((Component)localPlayer).transform.position, PluginConfig.Range.Value, PluginConfig.MaxScanned.Value, num, Buffer); _inRange = ContainerRegistry.LastInRange; _usable = ContainerRegistry.LastUsable; int value = PluginConfig.MaxContainersPerAction.Value; _matching = 0; foreach (Container item in Buffer) { if (!((Object)(object)item == (Object)(object)val) && HasAnythingFor(item, localPlayer)) { _matching++; if (_requested < value) { _requested++; _awaiting++; Requested[item] = new Request { ActionId = _actionId, ExpiresAt = realtimeSinceStartup + 60f }; item.m_nview.InvokeRPC("RPC_RequestStack", new object[1] { num }); } } } _syncMs = (Time.realtimeSinceStartup - realtimeSinceStartup) * 1000f; } finally { _dispatching = false; } if (_awaiting == 0) { Report(); } } public static bool TryHandleResponse(Container container, bool granted) { if ((Object)(object)container == (Object)null || !Requested.TryGetValue(container, out var value)) { return false; } Requested.Remove(container); if (value.ActionId != _actionId) { return true; } if (granted) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null && (Object)(object)container.m_nview != (Object)null && container.m_nview.IsValid()) { container.m_nview.ClaimOwnership(); int num = StackInto(container, localPlayer); if (num > 0) { _movedTotal += num; _containersTouched++; } } } else { _denied++; } if (_awaiting > 0) { _awaiting--; } if (!_dispatching && _awaiting == 0) { Report(); } return true; } public static void Tick() { float realtimeSinceStartup = Time.realtimeSinceStartup; if (_running && realtimeSinceStartup >= _deadline) { _awaiting = 0; Report(); } PruneExpiredRequests(realtimeSinceStartup); } public static void Reset() { Requested.Clear(); Buffer.Clear(); ItemBuffer.Clear(); _awaiting = 0; _requested = 0; _matching = 0; _dispatching = false; _running = false; _reported = true; _dragged = null; } private static void PruneExpiredRequests(float now) { if (Requested.Count == 0) { return; } PruneBuffer.Clear(); foreach (KeyValuePair<Container, Request> item in Requested) { if ((Object)(object)item.Key == (Object)null || now >= item.Value.ExpiresAt) { PruneBuffer.Add(item.Key); } } foreach (Container item2 in PruneBuffer) { Requested.Remove(item2); } PruneBuffer.Clear(); } private static bool HasAnythingFor(Container container, Player player) { Inventory inventory = container.GetInventory(); Inventory inventory2 = ((Humanoid)player).GetInventory(); if (inventory == null || inventory2 == null) { return false; } List<ItemData> allItems = inventory2.GetAllItems(); for (int i = 0; i < allItems.Count; i++) { ItemData val = allItems[i]; if (CanStash(val, player) && inventory.ContainsItemByName(val.m_shared.m_name)) { return true; } } return false; } private static int StackInto(Container container, Player player) { ZNetView nview = container.m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid() || !nview.IsOwner()) { return 0; } Inventory inventory = container.GetInventory(); Inventory inventory2 = ((Humanoid)player).GetInventory(); if (inventory == null || inventory2 == null) { return 0; } int num = 0; Action onChanged = inventory.m_onChanged; inventory.m_onChanged = null; try { ItemBuffer.Clear(); ItemBuffer.AddRange(inventory2.GetAllItems()); MovedBuffer.Clear(); foreach (ItemData item in ItemBuffer) { if (CanStash(item, player) && inventory.ContainsItemByName(item.m_shared.m_name)) { int stack = item.m_stack; int num2; if (inventory.AddItem(item)) { inventory2.RemoveItem(item); num2 = stack; } else { num2 = stack - item.m_stack; } if (num2 > 0) { num += num2; string key = MoveLog.Localize(item.m_shared.m_name); MovedBuffer.TryGetValue(key, out var value); MovedBuffer[key] = value + num2; } } } } finally { ItemBuffer.Clear(); inventory.m_onChanged = onChanged; } if (num > 0) { inventory.Changed(false, false); inventory2.Changed(false, false); MoveLog.Record(intoContainer: true, container, MovedBuffer); } MovedBuffer.Clear(); return num; } private static bool CanStash(ItemData item, Player player) { if (item?.m_shared == null || item == _dragged) { return false; } if (((Humanoid)player).IsItemEquiped(item)) { return false; } if (PluginConfig.ProtectHotbar.Value && item.m_gridPos.y == 0) { return false; } if (FavoriteStore.IsProtected(item)) { return false; } return !PluginConfig.IsExcluded(item); } private static void Report() { //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) if (_reported) { return; } _reported = true; _running = false; _dragged = null; Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return; } if (PluginConfig.DebugTiming.Value) { float num = (Time.realtimeSinceStartup - _startedAt) * 1000f; Plugin.Log.LogInfo((object)($"[stash] {_movedTotal} items -> {_containersTouched} cofres | " + $"en rango {_inRange} | utilizables {_usable} | " + $"con coincidencia {_matching} | pedidos {_requested} (tope {PluginConfig.MaxContainersPerAction.Value}) | " + $"sincrono {_syncMs:F2} ms | total {num:F0} ms | rechazados {_denied} | " + $"registro {ContainerRegistry.Count} cofres | peticiones vivas {Requested.Count}")); } if (_movedTotal > 0) { ((Character)localPlayer).Message((MessageType)2, $"Guardado: {_movedTotal} en {_containersTouched} cofre(s)", 0, (Sprite)null, false); if ((Object)(object)InventoryGui.instance != (Object)null) { InventoryGui.instance.m_moveItemEffects.Create(((Component)localPlayer).transform.position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID)); } } else if (_denied > 0) { ((Character)localPlayer).Message((MessageType)2, $"Sin guardar: {_denied} cofre(s) en uso por otro jugador", 0, (Sprite)null, false); } else { ((Character)localPlayer).Message((MessageType)2, "Nada que guardar cerca", 0, (Sprite)null, false); } } } [HarmonyPatch(typeof(Container), "RPC_StackResponse")] internal static class Container_RPC_StackResponse_Patch { private static bool Prefix(Container __instance, bool granted) { try { return !StashService.TryHandleResponse(__instance, granted); } catch (Exception arg) { Plugin.Log.LogError((object)$"Fallo al procesar la respuesta de apilado: {arg}"); return false; } } } internal static class StationFeeder { private static readonly List<Container> Buffer = new List<Container>(16); private static readonly List<Container> DiagBuffer = new List<Container>(16); private static readonly List<ItemData> ItemBuffer = new List<ItemData>(32); private static readonly Dictionary<Smelter, float> NextCheck = new Dictionary<Smelter, float>(); private static readonly List<Smelter> PruneBuffer = new List<Smelter>(8); private static float _budgetWindowStart; private static int _stationsInWindow; public static void Tick(Smelter smelter) { //IL_00da: Unknown result type (might be due to invalid IL or missing references) if (!PluginConfig.Enabled.Value || !PluginConfig.FeedStations.Value) { return; } ZNetView nview = smelter.m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid()) { return; } float realtimeSinceStartup = Time.realtimeSinceStartup; if (NextCheck.TryGetValue(smelter, out var value) && realtimeSinceStartup < value) { return; } NextCheck[smelter] = realtimeSinceStartup + PluginConfig.FeedIntervalSeconds.Value; PruneStale(realtimeSinceStartup); bool flag = (Object)(object)smelter.m_fuelItem != (Object)null && smelter.m_maxFuel > 0 && smelter.GetFuel() < (float)smelter.m_maxFuel; bool flag2 = smelter.GetQueueSize() < smelter.m_maxOre; bool flag3 = nview.IsOwner(); if (!flag3 || (!flag && !flag2)) { Diagnose(smelter, flag3, flag, flag2, 0L); } else { if (!TakeGlobalBudget(realtimeSinceStartup)) { return; } long playerId = ContainerAccess.LocalPlayerId(); ContainerRegistry.Query(((Component)smelter).transform.position, PluginConfig.FeedRange.Value, PluginConfig.MaxScanned.Value, playerId, Buffer); int num = PluginConfig.FeedMaxPerCycle.Value; foreach (Container item in Buffer) { if (num <= 0) { break; } if (!((Object)(object)item.m_nview == (Object)null) && item.m_nview.IsOwner()) { num -= FeedFrom(smelter, item, num); } } if (num == PluginConfig.FeedMaxPerCycle.Value) { Diagnose(smelter, owner: true, flag, flag2, playerId); } } } private static void Diagnose(Smelter smelter, bool owner, bool wantsFuel, bool wantsOre, long playerId) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) if (!PluginConfig.DebugTiming.Value) { return; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return; } float num = Vector3.Distance(((Component)smelter).transform.position, ((Component)localPlayer).transform.position); if (num > 30f) { return; } string arg = MoveLog.Localize(smelter.m_name); if (!owner) { Plugin.Log.LogInfo((object)($"[horno] {arg} a {num:F1} m | dueno del ZDO: NO -> este cliente no lo alimenta " + "(lo tiene otro jugador o el servidor)")); return; } if (!wantsFuel && !wantsOre) { Plugin.Log.LogInfo((object)$"[horno] {arg} a {num:F1} m | lleno, no necesita nada"); return; } ContainerRegistry.Query(((Component)smelter).transform.position, PluginConfig.FeedRange.Value, PluginConfig.MaxScanned.Value, playerId, DiagBuffer); int num2 = 0; foreach (Container item in DiagBuffer) { if ((Object)(object)item.m_nview != (Object)null && item.m_nview.IsOwner()) { num2++; } } int lastInRange = ContainerRegistry.LastInRange; int lastUsable = ContainerRegistry.LastUsable; string text = ((lastInRange == 0) ? $"no hay ningun cofre a menos de {PluginConfig.FeedRange.Value:F0} m" : ((lastUsable < lastInRange) ? ($"de {lastInRange} cofre(s) cerca, {lastInRange - lastUsable} quedaron descartados por " + "permisos, ward, o por estar marcados EN USO (un cofre con la tapa levantada que nadie abrio tiene la marca trabada: rompelo y volvelo a poner)") : ((num2 != 0) ? $"hay {num2} cofre(s) tuyo(s) pero ninguno tiene material que este horno acepte" : ($"hay {lastUsable} cofre(s) utilizable(s) pero ninguno es tuyo " + "(en el servidor la propiedad del ZDO puede estar en otro jugador)")))); Plugin.Log.LogInfo((object)(string.Format("[horno] {0} a {1:F1} m | quiere {2}", arg, num, wantsFuel ? "combustible" : "") + ((wantsFuel && wantsOre) ? " y " : "") + (wantsOre ? "mineral" : "") + " | " + $"en rango {lastInRange} | utilizables {lastUsable} | tuyos {num2} -> {text}")); } private static bool TakeGlobalBudget(float now) { if (now - _budgetWindowStart >= 1f) { _budgetWindowStart = now; _stationsInWindow = 0; } if (_stationsInWindow >= PluginConfig.FeedMaxStationsPerSecond.Value) { return false; } _stationsInWindow++; return true; } private static int FeedFrom(Smelter smelter, Container container, int budget) { Inventory inventory = container.GetInventory(); if (inventory == null) { return 0; } string text = (((Object)(object)smelter.m_fuelItem != (Object)null) ? ((Object)((Component)smelter.m_fuelItem).gameObject).name : null); int num = 0; Action onChanged = inventory.m_onChanged; inventory.m_onChanged = null; try { ItemBuffer.Clear(); ItemBuffer.AddRange(inventory.GetAllItems()); foreach (ItemData item in ItemBuffer) { if (num >= budget) { break; } if (item?.m_shared != null && !((Object)(object)item.m_dropPrefab == (Object)null) && item.m_stack > 0 && !PluginConfig.IsExcluded(item)) { string name = ((Object)item.m_dropPrefab).name; bool flag = text != null && name == text; bool flag2 = !flag && smelter.IsItemAllowed(name); if (flag || flag2) { num += (flag ? FeedFuel(smelter, container, inventory, item, budget - num) : FeedOre(smelter, container, inventory, item, name, budget - num)); } } } } finally { ItemBuffer.Clear(); inventory.m_onChanged = onChanged; if (num > 0) { inventory.Changed(false, false); } } return num; } private static int FeedFuel(Smelter smelter, Container container, Inventory source, ItemData item, int budget) { int num = Mathf.Min(Mathf.Min(Mathf.FloorToInt((float)smelter.m_maxFuel - smelter.GetFuel()), item.m_stack), budget); int num2 = 0; for (int i = 0; i < num; i++) { if (!Move(smelter, source, item, 1)) { break; } num2++; smelter.m_nview.InvokeRPC("RPC_AddFuel", Array.Empty<object>()); } if (num2 > 0) { MoveLog.RecordStationFeed(container, item.m_shared.m_name, num2); } return num2; } private static int FeedOre(Smelter smelter, Container container, Inventory source, ItemData item, string prefabName, int budget) { int num = Mathf.Min(Mathf.Min(smelter.m_maxOre - smelter.GetQueueSize(), item.m_stack), budget); bool cheated = item.m_cheated; int num2 = 0; for (int i = 0; i < num; i++) { if (!Move(smelter, source, item, 1)) { break; } num2++; smelter.m_nview.InvokeRPC("RPC_AddOre", new object[2] { prefabName, cheated }); } if (num2 > 0) { MoveLog.RecordStationFeed(container, item.m_shared.m_name, num2); } return num2; } private static bool Move(Smelter smelter, Inventory source, ItemData item, int amount) { if ((Object)(object)smelter.m_nview == (Object)null || !smelter.m_nview.IsValid() || !smelter.m_nview.IsOwner()) { return false; } return source.RemoveItem(item, amount); } private static void PruneStale(float now) { if (NextCheck.Count < 64) { return; } PruneBuffer.Clear(); foreach (KeyValuePair<Smelter, float> item in NextCheck) { if ((Object)(object)item.Key == (Object)null || now > item.Value + 300f) { PruneBuffer.Add(item.Key); } } foreach (Smelter item2 in PruneBuffer) { NextCheck.Remove(item2); } PruneBuffer.Clear(); } public static void Reset() { NextCheck.Clear(); Buffer.Clear(); DiagBuffer.Clear(); ItemBuffer.Clear(); _stationsInWindow = 0; _budgetWindowStart = 0f; } } [HarmonyPatch(typeof(Smelter), "UpdateSmelter")] internal static class Smelter_UpdateSmelter_Patch { private static void Postfix(Smelter __instance) { try { StationFeeder.Tick(__instance); } catch (Exception arg) { Plugin.Log.LogError((object)$"Fallo al cargar el horno desde los cofres: {arg}"); } } } } namespace QuickStash.Core { internal static class AnimalDiagnostics { private const float IntervalSeconds = 5f; private const float ScanRange = 25f; private const int MaxReported = 5; private static readonly StringBuilder Builder = new StringBuilder(160); private static readonly List<Container> ContainerBuffer = new List<Container>(8); private static float _nextRun; private static bool _reportedEmpty; public static void Tick() { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) if (!PluginConfig.DebugTiming.Value) { return; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return; } float realtimeSinceStartup = Time.realtimeSinceStartup; if (realtimeSinceStartup < _nextRun) { return; } _nextRun = realtimeSinceStartup + 5f; List<BaseAI> instances = BaseAI.m_instances; if (instances == null) { Plugin.Log.LogWarning((object)"[diag] no se pudo leer la lista de IAs del juego."); return; } Vector3 position = ((Component)localPlayer).transform.position; float num = 625f; long playerId = ContainerAccess.LocalPlayerId(); int num2 = 0; int num3 = 0; for (int i = 0; i < instances.Count; i++) { BaseAI val = instances[i]; if ((Object)(object)val == (Object)null) { continue; } Tameable tamable = val.m_tamable; if ((Object)(object)tamable == (Object)null || !tamable.IsTamed()) { continue; } Vector3 val2 = ((Component)val).transform.position - position; if (!(((Vector3)(ref val2)).sqrMagnitude > num)) { num3++; if (num2 < 5) { num2++; Report(val, tamable, position, playerId); } } } if (num3 == 0) { if (!_reportedEmpty) { _reportedEmpty = true; Plugin.Log.LogInfo((object)$"[diag] no hay animales domesticados a menos de {25f:F0} m."); } } else { _reportedEmpty = false; if (num3 > num2) { Plugin.Log.LogInfo((object)$"[diag] ... y {num3 - num2} animal(es) mas, no listados."); } } } private static void Report(BaseAI ai, Tameable tameable, Vector3 origin, long playerId) { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Unknown result type (might be due to invalid IL or missing references) ZNetView nview = ai.m_nview; bool flag = (Object)(object)nview != (Object)null && nview.IsValid() && nview.IsOwner(); Builder.Length = 0; Builder.Append("[diag] ").Append(NameOf(ai)); Builder.Append(" a ").Append(Vector3.Distance(((Component)ai).transform.position, origin).ToString("F1")).Append(" m"); Builder.Append(" | dueno del ZDO: ").Append(flag ? "SI" : "NO"); Builder.Append(" | hambriento: ").Append(tameable.IsHungry() ? "si" : "no"); MonsterAI val = (MonsterAI)(object)((ai is MonsterAI) ? ai : null); if ((Object)(object)val == (Object)null) { Builder.Append(" | no es MonsterAI (no usa el sistema de comer del suelo)"); } else if (val.m_consumeItems == null || val.m_consumeItems.Count == 0) { Builder.Append(" | LISTA DE COMIDA VACIA (el juego nunca busca comida para este bicho)"); } else { Builder.Append(" | come ").Append(val.m_consumeItems.Count).Append(": "); for (int i = 0; i < val.m_consumeItems.Count; i++) { ItemDrop val2 = val.m_consumeItems[i]; if (!((Object)(object)val2 == (Object)null) && val2.m_itemData?.m_shared != null) { if (i > 0) { Builder.Append(", "); } Builder.Append(MoveLog.Localize(val2.m_itemData.m_shared.m_name)); } } } ContainerRegistry.Query(((Component)ai).transform.position, PluginConfig.AnimalFeedRange.Value, PluginConfig.MaxScanned.Value, playerId, ContainerBuffer); int num = 0; foreach (Container item in ContainerBuffer) { if ((Object)(object)item.m_nview != (Object)null && item.m_nview.IsOwner()) { num++; } } Builder.Append(" | cofres accesibles: ").Append(ContainerBuffer.Count).Append(" (tuyos: ") .Append(num) .Append(')'); Plugin.Log.LogInfo((object)Builder.ToString()); Builder.Length = 0; } private static string NameOf(BaseAI ai) { Character character = ai.m_character; if (!((Object)(object)character != (Object)null)) { return ((Object)ai).name; } return MoveLog.Localize(character.m_name); } public static void Reset() { _nextRun = 0f; _reportedEmpty = false; ContainerBuffer.Clear(); } } internal static class ContainerAccess { public static bool CanUse(Container container, long playerId) { //IL_0049: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)container == (Object)null) { return false; } ZNetView nview = container.m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid()) { return false; } if (IsInUse(container, nview)) { return false; } if (!container.CheckAccess(playerId)) { return false; } if (container.m_checkGuardStone && !PrivateArea.CheckAccess(((Component)container).transform.position, 0f, false, false)) { return false; } return true; } public static bool IsInUse(Container container, ZNetView nview) { if (nview.IsOwner()) { return container.m_inUse; } ZDO zDO = nview.GetZDO(); if (zDO != null) { return zDO.GetInt(ZDOVars.s_inUse, 0) == 1; } return false; } public static long LocalPlayerId() { if ((Object)(object)Game.instance == (Object)null) { return 0L; } PlayerProfile playerProfile = Game.instance.GetPlayerProfile(); if (playerProfile != null) { return playerProfile.GetPlayerID(); } return 0L; } } internal static class ContainerIndex { private readonly struct ItemKey : IEquatable<ItemKey> { public readonly string Name; public readonly int Quality; public ItemKey(string name, int quality) { Name = name; Quality = quality; } public bool Equals(ItemKey other) { if (Quality == other.Quality) { return Name == other.Name; } return false; } public override bool Equals(object obj) { if (obj is ItemKey other) { return Equals(other); } return false; } public override int GetHashCode() { return ((Name?.GetHashCode() ?? 0) * 397) ^ Quality; } } private static readonly Dictionary<ItemKey, int> Counts = new Dictionary<ItemKey, int>(64); private static readonly Dictionary<string, int> Totals = new Dictionary<string, int>(64); private static readonly List<Container> Nearby = new List<Container>(32); private static Vector3 _point; private static float _range = -1f; private static float _time = float.NegativeInfinity; private static bool _valid; public static IReadOnlyList<Container> Containers => Nearby; public static void Invalidate() { _valid = false; Nearby.Clear(); Counts.Clear(); Totals.Clear(); } public static void EnsureFresh(Vector3 point, float range, long playerId) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) float realtimeSinceStartup = Time.realtimeSinceStartup; float num = (float)PluginConfig.CacheMs.Value / 1000f; if (_valid && Mathf.Approximately(_range, range) && realtimeSinceStartup - _time <= num) { Vector3 val = point - _point; if (((Vector3)(ref val)).sqrMagnitude <= 1f) { return; } } ContainerRegistry.Query(point, range, PluginConfig.MaxScanned.Value, playerId, Nearby); Counts.Clear(); Totals.Clear(); foreach (Container item in Nearby) { Inventory inventory = item.GetInventory(); if (inventory == null) { continue; } foreach (ItemData allItem in inventory.GetAllItems()) { if (allItem?.m_shared != null && allItem.m_worldLevel >= Game.m_worldLevel) { ItemKey key = new ItemKey(allItem.m_shared.m_name, allItem.m_quality); Counts.TryGetValue(key, out var value); Counts[key] = value + allItem.m_stack; Totals.TryGetValue(allItem.m_shared.m_name, out var value2); Totals[allItem.m_shared.m_name] = value2 + allItem.m_stack; } } } _point = point; _range = range; _time = realtimeSinceStartup; _valid = true; } public static int Count(string name, int quality) { if (quality >= 0) { Counts.TryGetValue(new ItemKey(name, quality), out var value); return value; } Totals.TryGetValue(name, out var value2); return value2; } } internal static class ContainerRegistry { private struct Entry { public Container Container; public float SqrDistance; } private static readonly List<Container> All = new List<Container>(256); private static readonly List<Entry> SortBuffer = new List<Entry>(64); private static readonly Comparison<Entry> ByDistance = (Entry a, Entry b) => a.SqrDistance.CompareTo(b.SqrDistance); public static int Count => All.Count; public static int LastInRange { get; private set; } public static int LastUsable { get; private set; } public static void Register(Container container) { if ((Object)(object)container != (Object)null) { All.Add(container); } } public static void Unregister(Container container) { All.Remove(container); } public static void Clear() { All.Clear(); } public static void Query(Vector3 point, float range, int maxScanned, long playerId, List<Container> results) { //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: 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) //IL_0060: Unknown result type (might be due to invalid IL or missing references) results.Clear(); SortBuffer.Clear(); LastInRange = 0; float num = range * range; for (int num2 = All.Count - 1; num2 >= 0; num2--) { Container val = All[num2]; if ((Object)(object)val == (Object)null) { All.RemoveAt(num2); } else { Vector3 val2 = ((Component)val).transform.position - point; float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude; if (!(sqrMagnitude > num)) { LastInRange++; if (ContainerAccess.CanUse(val, playerId)) { SortBuffer.Add(new Entry { Container = val, SqrDistance = sqrMagnitude }); } } } } LastUsable = SortBuffer.Count; SortBuffer.Sort(ByDistance); int num3 = Mathf.Min(maxScanned, SortBuffer.Count); for (int i = 0; i < num3; i++) { results.Add(SortBuffer[i].Container); } SortBuffer.Clear(); } } [HarmonyPatch(typeof(Container), "Awake")] internal static class Container_Awake_Patch { private static void Postfix(Container __instance) { ContainerRegistry.Register(__instance); } } [HarmonyPatch(typeof(Container), "OnDestroyed")] internal static class Container_OnDestroyed_Patch { private static void Postfix(Container __instance) { ContainerRegistry.Unregister(__instance); } } [HarmonyPatch(typeof(Player), "OnSpawned")] internal static class Player_OnSpawned_Patch { private static void Postfix(Player __instance) { if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer) { FavoriteStore.LoadForCurrentProfile(); } } } [HarmonyPatch(typeof(Game), "Logout")] internal static class Game_Logout_Patch { private static void Prefix() { FavoriteStore.FlushIfDirty(); ContainerRegistry.Clear(); ContainerIndex.Invalidate(); StashService.Reset(); StationFeeder.Reset(); AnimalFeeder.Reset(); AnimalDiagnostics.Reset(); } } internal static class MoveLog { private static readonly StringBuilder Builder = new StringBuilder(128); public static void Record(bool intoContainer, Container container, Dictionary<string, int> items) { if (!PluginConfig.LogMoves.Value || items == null || items.Count == 0) { return; } try { Builder.Length = 0; foreach (KeyValuePair<string, int> item in items) { if (Builder.Length > 0) { Builder.Append(", "); } Builder.Append(item.Key).Append(" x").Append(item.Value); } string text = (intoContainer ? "GUARDADO ->" : "SACADO <-"); Plugin.Log.LogInfo((object)$"{text} {Describe(container)} | {DateTime.Now:HH:mm:ss} | {Builder}"); } catch (Exception ex) { Plugin.Log.LogWarning((object)("No se pudo registrar el movimiento: " + ex.Message)); } finally { Builder.Length = 0; } } public static void RecordSingle(bool intoContainer, Container container, string itemName, int amount) { Write(intoContainer ? "GUARDADO ->" : "SACADO <-", container, itemName, amount); } public static void RecordStationFeed(Container container, string itemName, int amount) { if (PluginConfig.FeedLog.Value) { Write("HORNO <-", container, itemName, amount); } } public static void RecordAnimalFeed(Container container, string itemName, int amount) { if (PluginConfig.AnimalLog.Value) { Write("ANIMAL <-", container, itemName, amount); } } private static void Write(string label, Container container, string itemName, int amount) { if (!PluginConfig.LogMoves.Value || amount <= 0) { return; } try { Plugin.Log.LogInfo((object)$"{label} {Describe(container)} | {DateTime.Now:HH:mm:ss} | {Localize(itemName)} x{amount}"); } catch (Exception ex) { Plugin.Log.LogWarning((object)("No se pudo registrar el movimiento: " + ex.Message)); } } private static string Describe(Container container) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0036: 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_0052: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)container == (Object)null) { return "cofre desconocido"; } Vector3 position = ((Component)container).transform.position; return $"{Localize(container.m_name)} ({position.x:F0}, {position.y:F0}, {position.z:F0})"; } public static string Localize(string token) { if (string.IsNullOrEmpty(token)) { return "?"; } if (Localization.instance == null) { return token; } return Localization.instance.Localize(token); } } } namespace Quick