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 ManualStationPull v0.2.5
BepInEx/plugins/ManualStationPull/ManualStationPull.dll
Decompiled 2 days agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.UI; using Zephyrfit.ManualStationPull.Core; using Zephyrfit.ManualStationPull.Models; using Zephyrfit.ManualStationPull.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("ManualStationPull")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.2.5.0")] [assembly: AssemblyInformationalVersion("0.2.5")] [assembly: AssemblyProduct("ManualStationPull")] [assembly: AssemblyTitle("ManualStationPull")] [assembly: AssemblyVersion("0.2.5.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace Zephyrfit.ManualStationPull { [BepInPlugin("Zephyrfit.ManualStationPull", "Manual Station Pull", "0.2.5")] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class ManualStationPullPlugin : BaseUnityPlugin { public const string PluginGuid = "Zephyrfit.ManualStationPull"; public const string PluginName = "Manual Station Pull"; public const string PluginVersion = "0.2.5"; private Harmony _harmony; internal static ManualStationPullPlugin Instance { get; private set; } internal static ManualLogSource Log { get; private set; } internal ModConfig Settings { get; private set; } internal ContainerScanner Scanner { get; private set; } internal SelectionStore Selections { get; private set; } internal StationInputService Stations { get; private set; } internal SelectorHotkeyService SelectorHotkey { get; private set; } internal OreSelectorUI SelectorUI { get; private set; } private void Awake() { //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; Settings = new ModConfig(((BaseUnityPlugin)this).Config); Scanner = new ContainerScanner(Settings); Selections = new SelectionStore(Settings); Stations = new StationInputService(Settings, Scanner, Selections); SelectorHotkey = new SelectorHotkeyService(Settings, Stations); SelectorUI = ((Component)this).gameObject.AddComponent<OreSelectorUI>(); SelectorUI.Initialize(Settings, Stations, Selections); _harmony = new Harmony("Zephyrfit.ManualStationPull"); _harmony.PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Manual Station Pull 0.2.5 loaded."); } private void Update() { SelectorUI?.Tick(); SelectorHotkey?.Tick(); } private void OnDestroy() { SelectorUI?.Close(); Selections?.Flush(); Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } _harmony = null; Instance = null; } internal static bool IsFrenchLanguage() { try { Localization instance = Localization.instance; string text = ((instance != null) ? instance.GetSelectedLanguage() : null); if (string.IsNullOrWhiteSpace(text)) { return false; } text = text.Trim(); return text.Equals("French", StringComparison.OrdinalIgnoreCase) || text.Equals("Français", StringComparison.OrdinalIgnoreCase) || text.Equals("Francais", StringComparison.OrdinalIgnoreCase) || text.Equals("fr", StringComparison.OrdinalIgnoreCase) || text.StartsWith("fr-", StringComparison.OrdinalIgnoreCase) || text.StartsWith("fr_", StringComparison.OrdinalIgnoreCase); } catch { return false; } } internal static string L(string fr, string en) { if (!IsFrenchLanguage()) { return en; } return fr; } } } namespace Zephyrfit.ManualStationPull.UI { internal sealed class OreSelectorUI : MonoBehaviour { private ModConfig _settings; private StationInputService _stations; private SelectionStore _selections; private Canvas _canvas; private GameObject _panel; private RectTransform _content; private Text _title; private Text _subtitle; private Font _font; private Button _firstSelectable; private Text _closeButtonText; private Smelter _smelter; private Switch _inputSwitch; private Player _player; private List<InputCandidate> _candidates; private bool _previousCursorVisible; private CursorLockMode _previousCursorLock; internal bool IsOpen { get { if ((Object)(object)_panel != (Object)null) { return _panel.activeSelf; } return false; } } internal void Initialize(ModConfig settings, StationInputService stations, SelectionStore selections) { _settings = settings; _stations = stations; _selections = selections; BuildUI(); } internal void Tick() { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) if (IsOpen) { if (((int)_settings.CloseSelectorKey.Value != 0 && Input.GetKeyDown(_settings.CloseSelectorKey.Value)) || ZInput.GetButtonDown("JoyMenu")) { Close(); } else if ((Object)(object)_smelter == (Object)null || (Object)(object)_player == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)_player != (Object)(object)Player.m_localPlayer) { Close(); } else if (Vector3.Distance(((Component)_player).transform.position, ((Component)_smelter).transform.position) > 6f) { Close(); } } } internal void Open(Smelter smelter, Switch inputSwitch, Player player, List<InputCandidate> candidates) { //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) if ((Object)(object)_panel == (Object)null) { BuildUI(); } _smelter = smelter; _inputSwitch = inputSwitch; _player = player; _candidates = candidates ?? new List<InputCandidate>(); RefreshLocalizedText(); RebuildRows(); _previousCursorVisible = Cursor.visible; _previousCursorLock = Cursor.lockState; Cursor.visible = true; Cursor.lockState = (CursorLockMode)0; _panel.SetActive(true); if ((Object)(object)EventSystem.current != (Object)null && (Object)(object)_firstSelectable != (Object)null && ((Selectable)_firstSelectable).interactable) { EventSystem.current.SetSelectedGameObject(((Component)_firstSelectable).gameObject); } } internal void Close() { //IL_0050: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_panel == (Object)null) && _panel.activeSelf) { _panel.SetActive(false); _smelter = null; _inputSwitch = null; _player = null; _candidates = null; Cursor.visible = _previousCursorVisible; Cursor.lockState = _previousCursorLock; } } private void BuildUI() { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Expected O, but got Unknown //IL_00a2: Unknown result type (might be due to invalid IL or missing references) _font = FindValheimFont(); GameObject val = new GameObject("ManualStationPull_Canvas", new Type[4] { typeof(RectTransform), typeof(Canvas), typeof(CanvasScaler), typeof(GraphicRaycaster) }); val.transform.SetParent(((Component)this).transform, false); _canvas = val.GetComponent<Canvas>(); _canvas.renderMode = (RenderMode)0; _canvas.sortingOrder = 5000; CanvasScaler component = val.GetComponent<CanvasScaler>(); component.uiScaleMode = (ScaleMode)1; component.referenceResolution = new Vector2(1920f, 1080f); component.screenMatchMode = (ScreenMatchMode)0; component.matchWidthOrHeight = 0.5f; _panel = CreatePanel(val.transform); _panel.SetActive(false); } private GameObject CreatePanel(Transform parent) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0074: 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_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Expected O, but got Unknown //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_01eb: Unknown result type (might be due to invalid IL or missing references) //IL_027a: Unknown result type (might be due to invalid IL or missing references) //IL_02dd: Unknown result type (might be due to invalid IL or missing references) //IL_02f7: Unknown result type (might be due to invalid IL or missing references) //IL_0311: Unknown result type (might be due to invalid IL or missing references) //IL_0321: Unknown result type (might be due to invalid IL or missing references) //IL_0331: Unknown result type (might be due to invalid IL or missing references) //IL_03ea: Unknown result type (might be due to invalid IL or missing references) //IL_03f4: Expected O, but got Unknown GameObject val = CreateUIObject("SelectorPanel", parent, typeof(Image), typeof(VerticalLayoutGroup), typeof(ContentSizeFitter)); RectTransform component = val.GetComponent<RectTransform>(); component.anchorMin = new Vector2(0.5f, 0.5f); component.anchorMax = new Vector2(0.5f, 0.5f); component.pivot = new Vector2(0.5f, 0.5f); component.sizeDelta = new Vector2(560f, 480f); ((Graphic)val.GetComponent<Image>()).color = new Color(0.055f, 0.045f, 0.035f, 0.96f); VerticalLayoutGroup component2 = val.GetComponent<VerticalLayoutGroup>(); ((LayoutGroup)component2).padding = new RectOffset(24, 24, 20, 20); ((HorizontalOrVerticalLayoutGroup)component2).spacing = 10f; ((LayoutGroup)component2).childAlignment = (TextAnchor)1; ((HorizontalOrVerticalLayoutGroup)component2).childControlHeight = true; ((HorizontalOrVerticalLayoutGroup)component2).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)component2).childForceExpandHeight = false; ((HorizontalOrVerticalLayoutGroup)component2).childForceExpandWidth = true; ContentSizeFitter component3 = val.GetComponent<ContentSizeFitter>(); component3.horizontalFit = (FitMode)0; component3.verticalFit = (FitMode)0; _title = CreateText("Title", val.transform, 26, (FontStyle)1, (TextAnchor)4); AddLayout(((Component)_title).gameObject, 42f); _subtitle = CreateText("Subtitle", val.transform, 15, (FontStyle)0, (TextAnchor)4); ((Graphic)_subtitle).color = new Color(0.84f, 0.84f, 0.84f, 1f); AddLayout(((Component)_subtitle).gameObject, 52f); GameObject val2 = CreateUIObject("Scroll", val.transform, typeof(Image), typeof(ScrollRect), typeof(LayoutElement)); ((Graphic)val2.GetComponent<Image>()).color = new Color(0.02f, 0.02f, 0.02f, 0.32f); LayoutElement component4 = val2.GetComponent<LayoutElement>(); component4.preferredHeight = 300f; component4.flexibleHeight = 1f; GameObject val3 = CreateUIObject("Viewport", val2.transform, typeof(Image), typeof(Mask)); Stretch(val3.GetComponent<RectTransform>(), 8f, 8f, 8f, 8f); ((Graphic)val3.GetComponent<Image>()).color = new Color(1f, 1f, 1f, 0.01f); val3.GetComponent<Mask>().showMaskGraphic = false; GameObject val4 = CreateUIObject("Content", val3.transform, typeof(VerticalLayoutGroup), typeof(ContentSizeFitter)); _content = val4.GetComponent<RectTransform>(); _content.anchorMin = new Vector2(0f, 1f); _content.anchorMax = new Vector2(1f, 1f); _content.pivot = new Vector2(0.5f, 1f); _content.anchoredPosition = Vector2.zero; _content.sizeDelta = Vector2.zero; VerticalLayoutGroup component5 = val4.GetComponent<VerticalLayoutGroup>(); ((HorizontalOrVerticalLayoutGroup)component5).spacing = 6f; ((HorizontalOrVerticalLayoutGroup)component5).childControlHeight = true; ((HorizontalOrVerticalLayoutGroup)component5).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)component5).childForceExpandHeight = false; ((HorizontalOrVerticalLayoutGroup)component5).childForceExpandWidth = true; val4.GetComponent<ContentSizeFitter>().verticalFit = (FitMode)2; ScrollRect component6 = val2.GetComponent<ScrollRect>(); component6.viewport = val3.GetComponent<RectTransform>(); component6.content = _content; component6.horizontal = false; component6.vertical = true; component6.movementType = (MovementType)2; component6.scrollSensitivity = 28f; Button val5 = CreateButton(val.transform, ManualStationPullPlugin.L("Fermer", "Close"), selected: false); _closeButtonText = ((Component)val5).GetComponentInChildren<Text>(true); ((UnityEvent)val5.onClick).AddListener(new UnityAction(Close)); AddLayout(((Component)val5).gameObject, 38f); return val; } private void RefreshLocalizedText() { if ((Object)(object)_title != (Object)null) { _title.text = ManualStationPullPlugin.L("Choisir le minerai", "Select furnace input"); } if ((Object)(object)_subtitle != (Object)null) { _subtitle.text = ManualStationPullPlugin.L("Le choix est mémorisé pour cette fonderie. Les quantités incluent l'inventaire et les coffres accessibles à proximité.", "The choice is remembered for this furnace. Quantities include your inventory and accessible nearby containers."); } if ((Object)(object)_closeButtonText != (Object)null) { _closeButtonText.text = ManualStationPullPlugin.L("Fermer", "Close"); } } private void RebuildRows() { //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Expected O, but got Unknown //IL_015d: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_content == (Object)null) { return; } for (int num = ((Transform)_content).childCount - 1; num >= 0; num--) { Object.Destroy((Object)(object)((Component)((Transform)_content).GetChild(num)).gameObject); } _firstSelectable = null; string prefabName = null; _selections.TryGet(_smelter, _player, out prefabName); foreach (InputCandidate candidate in _candidates) { InputCandidate captured = candidate; bool selected = string.Equals(prefabName, candidate.PrefabName, StringComparison.Ordinal); Button val = CreateCandidateButton((Transform)(object)_content, candidate, selected); ((Selectable)val).interactable = candidate.Available; ((UnityEvent)val.onClick).AddListener((UnityAction)delegate { Select(captured); }); if ((Object)(object)_firstSelectable == (Object)null && ((Selectable)val).interactable) { _firstSelectable = val; } } if (_candidates.Count == 0) { Text obj = CreateText("Empty", (Transform)(object)_content, 17, (FontStyle)2, (TextAnchor)4); obj.text = ManualStationPullPlugin.L("Aucune ressource compatible disponible.", "No compatible resource is available."); ((Graphic)obj).color = new Color(0.75f, 0.75f, 0.75f, 1f); AddLayout(((Component)obj).gameObject, 50f); } } private Button CreateCandidateButton(Transform parent, InputCandidate candidate, bool selected) { //IL_007c: 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_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: 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_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Expected O, but got Unknown //IL_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_01c9: Unknown result type (might be due to invalid IL or missing references) //IL_0247: Unknown result type (might be due to invalid IL or missing references) //IL_0240: Unknown result type (might be due to invalid IL or missing references) //IL_02f6: Unknown result type (might be due to invalid IL or missing references) //IL_02db: Unknown result type (might be due to invalid IL or missing references) GameObject val = CreateUIObject("Input_" + candidate.PrefabName, parent, typeof(Image), typeof(Button), typeof(LayoutElement)); ((Graphic)val.GetComponent<Image>()).color = (selected ? new Color(0.29f, 0.38f, 0.23f, 0.96f) : new Color(0.18f, 0.15f, 0.11f, 0.96f)); AddLayout(val, 58f); Button component = val.GetComponent<Button>(); ColorBlock colors = ((Selectable)component).colors; ((ColorBlock)(ref colors)).highlightedColor = new Color(0.34f, 0.29f, 0.2f, 1f); ((ColorBlock)(ref colors)).pressedColor = new Color(0.24f, 0.21f, 0.15f, 1f); ((ColorBlock)(ref colors)).disabledColor = new Color(0.1f, 0.1f, 0.1f, 0.6f); ((Selectable)component).colors = colors; HorizontalLayoutGroup obj = val.AddComponent<HorizontalLayoutGroup>(); ((LayoutGroup)obj).padding = new RectOffset(10, 12, 6, 6); ((HorizontalOrVerticalLayoutGroup)obj).spacing = 10f; ((LayoutGroup)obj).childAlignment = (TextAnchor)3; ((HorizontalOrVerticalLayoutGroup)obj).childControlHeight = true; ((HorizontalOrVerticalLayoutGroup)obj).childControlWidth = false; ((HorizontalOrVerticalLayoutGroup)obj).childForceExpandHeight = false; ((HorizontalOrVerticalLayoutGroup)obj).childForceExpandWidth = false; GameObject obj2 = CreateUIObject("Icon", val.transform, typeof(Image), typeof(LayoutElement)); LayoutElement component2 = obj2.GetComponent<LayoutElement>(); component2.preferredWidth = 44f; component2.preferredHeight = 44f; Image component3 = obj2.GetComponent<Image>(); component3.sprite = candidate.Icon; component3.preserveAspect = true; ((Graphic)component3).color = (Color)(candidate.Available ? Color.white : new Color(1f, 1f, 1f, 0.35f)); Text obj3 = CreateText("Name", val.transform, 19, (FontStyle)1, (TextAnchor)3); obj3.text = (selected ? "✓ " : string.Empty) + candidate.DisplayName; ((Component)obj3).gameObject.AddComponent<LayoutElement>().flexibleWidth = 1f; ((Graphic)obj3).color = (Color)(candidate.Available ? Color.white : new Color(0.55f, 0.55f, 0.55f, 1f)); Text val2 = CreateText("Counts", val.transform, 15, (FontStyle)0, (TextAnchor)5); val2.text = "x" + candidate.Total + "\n" + ManualStationPullPlugin.L("Coffres", "Chests") + ": " + candidate.Nearby; ((Graphic)val2).color = (candidate.Available ? new Color(0.9f, 0.82f, 0.6f, 1f) : new Color(0.5f, 0.5f, 0.5f, 1f)); ((Component)val2).gameObject.AddComponent<LayoutElement>().preferredWidth = 105f; return component; } private void Select(InputCandidate candidate) { if (candidate != null && candidate.Available && !((Object)(object)_smelter == (Object)null) && !((Object)(object)_player == (Object)null)) { Smelter smelter = _smelter; Switch inputSwitch = _inputSwitch; Player player = _player; _selections.Set(smelter, player, candidate.PrefabName); Close(); if (!_stations.TryAddByPrefab(smelter, inputSwitch, player, candidate.PrefabName)) { ((Character)player).Message((MessageType)2, ManualStationPullPlugin.L("Impossible d'ajouter cette ressource.", "Could not add this resource."), 0, (Sprite)null, false); } } } private Text CreateText(string name, Transform parent, int size, FontStyle style, TextAnchor alignment) { //IL_0033: 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_0043: Unknown result type (might be due to invalid IL or missing references) Text component = CreateUIObject(name, parent, typeof(Text)).GetComponent<Text>(); component.font = _font; component.fontSize = size; component.fontStyle = style; component.alignment = alignment; ((Graphic)component).color = Color.white; component.horizontalOverflow = (HorizontalWrapMode)0; component.verticalOverflow = (VerticalWrapMode)0; ((Graphic)component).raycastTarget = false; return component; } private Button CreateButton(Transform parent, string label, bool selected) { //IL_0064: 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) GameObject val = CreateUIObject("Button", parent, typeof(Image), typeof(Button)); ((Graphic)val.GetComponent<Image>()).color = (selected ? new Color(0.28f, 0.36f, 0.22f, 1f) : new Color(0.22f, 0.18f, 0.13f, 1f)); Button component = val.GetComponent<Button>(); Text obj = CreateText("Label", val.transform, 17, (FontStyle)1, (TextAnchor)4); obj.text = label; Stretch(((Graphic)obj).rectTransform, 4f, 4f, 4f, 4f); return component; } private static GameObject CreateUIObject(string name, Transform parent, params Type[] components) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown GameObject val = new GameObject(name, new Type[1] { typeof(RectTransform) }); val.transform.SetParent(parent, false); foreach (Type type in components) { if (type != typeof(RectTransform)) { val.AddComponent(type); } } return val; } private static void AddLayout(GameObject obj, float preferredHeight) { (obj.GetComponent<LayoutElement>() ?? obj.AddComponent<LayoutElement>()).preferredHeight = preferredHeight; } private static void Stretch(RectTransform rect, float left, float right, float top, float bottom) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) rect.anchorMin = Vector2.zero; rect.anchorMax = Vector2.one; rect.offsetMin = new Vector2(left, bottom); rect.offsetMax = new Vector2(0f - right, 0f - top); } private static Font FindValheimFont() { try { Font val = ((IEnumerable<Font>)Resources.FindObjectsOfTypeAll<Font>()).FirstOrDefault((Func<Font, bool>)((Font f) => (Object)(object)f != (Object)null && (((Object)f).name.IndexOf("Averia", StringComparison.OrdinalIgnoreCase) >= 0 || ((Object)f).name.IndexOf("Norse", StringComparison.OrdinalIgnoreCase) >= 0))); if ((Object)(object)val != (Object)null) { return val; } } catch { } return Resources.GetBuiltinResource<Font>("Arial.ttf"); } } } namespace Zephyrfit.ManualStationPull.Patches { [HarmonyPatch(typeof(Container), "Awake")] internal static class ContainerAwakePatch { private static void Postfix(Container __instance) { ContainerRegistry.Register(__instance); } } [HarmonyPatch(typeof(Player), "TakeInput")] internal static class PlayerTakeInputPatch { private static bool Prefix(Player __instance, ref bool __result) { ManualStationPullPlugin instance = ManualStationPullPlugin.Instance; if (instance != null && instance.SelectorUI?.IsOpen == true && (Object)(object)__instance == (Object)(object)Player.m_localPlayer) { __result = false; return false; } return true; } } [HarmonyPatch(typeof(InventoryGui), "IsVisible")] internal static class InventoryGuiVisiblePatch { private static void Postfix(ref bool __result) { ManualStationPullPlugin instance = ManualStationPullPlugin.Instance; if (instance != null && instance.SelectorUI?.IsOpen == true) { __result = true; } } } [HarmonyPatch(typeof(Smelter), "OnAddOre", new Type[] { typeof(Switch), typeof(Humanoid), typeof(ItemData) })] [HarmonyPriority(600)] [HarmonyBefore(new string[] { "chazman.RunicCrafting" })] internal static class SmelterAddOrePatch { private static bool Prefix(Smelter __instance, Switch sw, Humanoid user, ItemData item, ref bool __result) { ManualStationPullPlugin instance = ManualStationPullPlugin.Instance; if ((Object)(object)instance == (Object)null || StationInputService.IsBypassing || !instance.Settings.Enabled.Value) { return true; } Player val = (Player)(object)((user is Player) ? user : null); if ((Object)(object)val == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)val != (Object)(object)Player.m_localPlayer) { return true; } bool flag = StationInputService.IsCharcoalKiln(__instance); if (flag && !instance.Settings.PullKilnWood.Value) { return true; } if (item != null) { return true; } if (instance.SelectorUI.IsOpen) { __result = true; return false; } List<InputCandidate> list = instance.Stations.GetCandidates(__instance, val).FindAll((InputCandidate c) => c.Available); if (flag) { InputCandidate inputCandidate = list.Find((InputCandidate c) => string.Equals(c.PrefabName, "Wood", StringComparison.Ordinal)); if (inputCandidate == null) { return true; } instance.Selections.Set(__instance, val, inputCandidate.PrefabName); __result = instance.Stations.TryAddByPrefab(__instance, sw, val, inputCandidate.PrefabName); return false; } string selectedPrefab; bool flag2 = instance.Selections.TryGet(__instance, val, out selectedPrefab); InputCandidate inputCandidate2 = (flag2 ? list.Find((InputCandidate c) => string.Equals(c.PrefabName, selectedPrefab, StringComparison.Ordinal)) : null); if (list.Count == 0) { string shortcutLabel = instance.SelectorHotkey.GetShortcutLabel(); ((Character)val).Message((MessageType)2, flag2 ? ManualStationPullPlugin.L("Le minerai sélectionné n'est plus disponible — " + shortcutLabel + " pour changer.", "The selected input is no longer available — press " + shortcutLabel + " to change it.") : ManualStationPullPlugin.L("Aucun minerai compatible disponible à proximité.", "No compatible input is available nearby."), 0, (Sprite)null, false); __result = false; return false; } if (!flag2 && list.Count == 1) { inputCandidate2 = list[0]; instance.Selections.Set(__instance, val, inputCandidate2.PrefabName); __result = instance.Stations.TryAddByPrefab(__instance, sw, val, inputCandidate2.PrefabName); return false; } if (!flag2) { ((Character)val).Message((MessageType)2, ManualStationPullPlugin.L("Aucun minerai sélectionné — " + instance.SelectorHotkey.GetShortcutLabel() + " pour choisir.", "No input selected — press " + instance.SelectorHotkey.GetShortcutLabel() + " to choose."), 0, (Sprite)null, false); __result = false; return false; } if (inputCandidate2 == null) { ((Character)val).Message((MessageType)2, ManualStationPullPlugin.L("Le minerai sélectionné n'est plus disponible — " + instance.SelectorHotkey.GetShortcutLabel() + " pour changer.", "The selected input is no longer available — press " + instance.SelectorHotkey.GetShortcutLabel() + " to change it."), 0, (Sprite)null, false); __result = false; return false; } __result = instance.Stations.TryAddByPrefab(__instance, sw, val, inputCandidate2.PrefabName); return false; } } [HarmonyPatch(typeof(Smelter), "OnHoverAddOre")] internal static class SmelterHoverOrePatch { private static void Postfix(Smelter __instance, ref string __result) { ManualStationPullPlugin instance = ManualStationPullPlugin.Instance; Player localPlayer = Player.m_localPlayer; if ((Object)(object)instance == (Object)null || (Object)(object)localPlayer == (Object)null || !instance.Settings.Enabled.Value) { return; } instance.SelectorHotkey?.MarkHovered(__instance); if (!instance.Settings.ShowHoverHint.Value) { return; } if (instance.Selections.TryGet(__instance, localPlayer, out var prefabName)) { ItemDrop val = instance.Stations.FindConversionDrop(__instance, prefabName); if ((Object)(object)val != (Object)null) { int availableCountCached = instance.Stations.GetAvailableCountCached(__instance, localPlayer, prefabName); __result = __result + "\n<color=#c6d8ff>" + ManualStationPullPlugin.L("Sélection", "Selected") + ": " + StationInputService.LocalizeItem(val) + " (" + availableCountCached + ")</color>"; } } else if (!StationInputService.IsCharcoalKiln(__instance)) { __result = __result + "\n<color=#c6d8ff>" + ManualStationPullPlugin.L("Sélection", "Selected") + ": —</color>"; } if (!StationInputService.IsCharcoalKiln(__instance)) { __result = __result + "\n[<color=yellow><b>" + instance.SelectorHotkey.GetShortcutLabel() + "</b></color>] " + ManualStationPullPlugin.L("Choisir le minerai", "Select input"); } } } } namespace Zephyrfit.ManualStationPull.Models { internal sealed class InputCandidate { internal string PrefabName; internal string SharedName; internal string DisplayName; internal Sprite Icon; internal int Carried; internal int Nearby; internal int Total; internal ItemDrop SourceDrop; internal bool Available => Total > 0; } } namespace Zephyrfit.ManualStationPull.Core { internal static class ContainerRegistry { private static readonly HashSet<Container> Containers = new HashSet<Container>(); internal static void Register(Container container) { if ((Object)(object)container != (Object)null) { Containers.Add(container); } } internal static List<Container> Snapshot() { Containers.RemoveWhere((Container c) => (Object)(object)c == (Object)null); return Containers.Where((Container c) => (Object)(object)c != (Object)null).ToList(); } internal static void Clear() { Containers.Clear(); } } internal sealed class ContainerScanner { private sealed class SpatialCacheEntry { internal Vector3 Origin; internal float CreatedAt; internal List<Container> Containers; } private readonly ModConfig _settings; private readonly Dictionary<string, SpatialCacheEntry> _spatialCache = new Dictionary<string, SpatialCacheEntry>(StringComparer.Ordinal); private static readonly MethodInfo ContainerCheckAccess = AccessTools.Method(typeof(Container), "CheckAccess", new Type[1] { typeof(long) }, (Type[])null); private static readonly MethodInfo ContainerLoad = AccessTools.Method(typeof(Container), "Load", (Type[])null, (Type[])null); private static readonly FieldInfo ContainerNView = AccessTools.Field(typeof(Container), "m_nview"); internal ContainerScanner(ModConfig settings) { _settings = settings; } internal List<Container> GetEligibleContainers(Vector3 origin, Player player, string cacheKey) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) List<Container> spatialCandidates = GetSpatialCandidates(origin, cacheKey); List<Container> list = new List<Container>(spatialCandidates.Count); foreach (Container item in spatialCandidates) { if (!((Object)(object)item == (Object)null) && !((Object)(object)((Component)item).transform == (Object)null) && !((Object)(object)((Component)item).GetComponentInParent<TombStone>() != (Object)null) && (!_settings.SkipInUseContainers.Value || !item.IsInUse()) && (!_settings.RespectContainerAccess.Value || CheckContainerAccess(item, player.GetPlayerID())) && (!_settings.RespectWards.Value || !item.m_checkGuardStone || PrivateArea.CheckAccess(((Component)item).transform.position, 0f, false, false))) { ZNetView containerView = GetContainerView(item); if (!((Object)(object)containerView == (Object)null) && containerView.IsValid() && containerView.GetZDO() != null && (!_settings.RespectMultiUserChestIgnore.Value || !containerView.GetZDO().GetBool("MUC_Ignore", false)) && GetLoadedInventory(item) != null) { list.Add(item); } } } return list; } private List<Container> GetSpatialCandidates(Vector3 origin, string cacheKey) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //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) string key = (string.IsNullOrEmpty(cacheKey) ? "global" : cacheKey); float unscaledTime = Time.unscaledTime; if (_spatialCache.TryGetValue(key, out var value) && unscaledTime - value.CreatedAt <= _settings.SpatialCacheSeconds.Value) { Vector3 val = value.Origin - origin; if (((Vector3)(ref val)).sqrMagnitude <= 1f) { return value.Containers.Where((Container c) => (Object)(object)c != (Object)null).ToList(); } } float radiusSqr = _settings.RangeMeters.Value * _settings.RangeMeters.Value; List<Container> list = ContainerRegistry.Snapshot().Where(delegate(Container c) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)c != (Object)null && (Object)(object)((Component)c).transform != (Object)null) { Vector3 val2 = ((Component)c).transform.position - origin; return ((Vector3)(ref val2)).sqrMagnitude <= radiusSqr; } return false; }).OrderBy(delegate(Container c) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) Vector3 val2 = ((Component)c).transform.position - origin; return ((Vector3)(ref val2)).sqrMagnitude; }) .ToList(); _spatialCache[key] = new SpatialCacheEntry { Origin = origin, CreatedAt = unscaledTime, Containers = list }; return list; } internal static bool CheckContainerAccess(Container container, long playerId) { if ((Object)(object)container == (Object)null || ContainerCheckAccess == null) { return false; } try { object obj = ContainerCheckAccess.Invoke(container, new object[1] { playerId }); return obj is bool && (bool)obj; } catch { return false; } } internal static Inventory GetLoadedInventory(Container container) { if ((Object)(object)container == (Object)null) { return null; } try { Inventory inventory = container.GetInventory(); if (inventory != null) { return inventory; } ContainerLoad?.Invoke(container, null); return container.GetInventory(); } catch { return null; } } internal static ZNetView GetContainerView(Container container) { if ((Object)(object)container == (Object)null) { return null; } try { if (ContainerNView != null) { object? value = ContainerNView.GetValue(container); ZNetView val = (ZNetView)((value is ZNetView) ? value : null); if ((Object)(object)val != (Object)null) { return val; } } } catch { } return ((Component)container).GetComponent<ZNetView>() ?? ((Component)container).GetComponentInParent<ZNetView>(); } } internal enum SelectionPersistenceMode { Session, World } internal sealed class ModConfig { internal readonly ConfigEntry<bool> Enabled; internal readonly ConfigEntry<float> RangeMeters; internal readonly ConfigEntry<float> SpatialCacheSeconds; internal readonly ConfigEntry<KeyCode> SelectorModifier; internal readonly ConfigEntry<KeyCode> SelectorKey; internal readonly ConfigEntry<KeyCode> CloseSelectorKey; internal readonly ConfigEntry<bool> ShowHoverHint; internal readonly ConfigEntry<bool> PullKilnWood; internal readonly ConfigEntry<bool> ShowUnavailableInputs; internal readonly ConfigEntry<SelectionPersistenceMode> SelectionPersistence; internal readonly ConfigEntry<bool> RespectContainerAccess; internal readonly ConfigEntry<bool> RespectWards; internal readonly ConfigEntry<bool> SkipInUseContainers; internal readonly ConfigEntry<bool> RespectMultiUserChestIgnore; internal readonly ConfigEntry<bool> DebugLogging; internal ModConfig(ConfigFile config) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Expected O, but got Unknown //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Expected O, but got Unknown Enabled = config.Bind<bool>("General", "Enabled", true, "Enable Manual Station Pull."); RangeMeters = config.Bind<float>("General", "RangeMeters", 20f, new ConfigDescription("Nearby-container search range in meters.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 50f), Array.Empty<object>())); SpatialCacheSeconds = config.Bind<float>("General", "SpatialCacheSeconds", 0.35f, new ConfigDescription("How long nearby-container spatial results are reused. Access checks still run on every interaction.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 2f), Array.Empty<object>())); SelectorModifier = config.Bind<KeyCode>("Controls", "SelectorModifier", (KeyCode)308, "Modifier used with SelectorKey to open the ore selector while aiming at a furnace input. Set None for a single-key shortcut."); SelectorKey = config.Bind<KeyCode>("Controls", "SelectorKey", (KeyCode)282, "Key used with SelectorModifier to open the ore selector while aiming at a furnace input."); CloseSelectorKey = config.Bind<KeyCode>("Controls", "CloseSelectorKey", (KeyCode)27, "Key used to close the selector."); ShowHoverHint = config.Bind<bool>("Controls", "ShowHoverHint", true, "Show current selection and selector shortcut in hover text."); PullKilnWood = config.Bind<bool>("Stations", "PullKilnWood", true, "Allow charcoal kilns to manually pull exact vanilla Wood through their normal input path. FineWood, RoundLog, YggdrasilWood and other woods are never offered or pulled by this feature."); ShowUnavailableInputs = config.Bind<bool>("Stations", "ShowUnavailableInputs", false, "Show station-compatible inputs with zero availability as disabled rows in the selector."); SelectionPersistence = config.Bind<SelectionPersistenceMode>("Selection", "Persistence", SelectionPersistenceMode.World, "Session = forget choices on restart. World = remember each local player's choice per world and station ZDO."); RespectContainerAccess = config.Bind<bool>("Safety", "RespectContainerAccess", true, "Respect Container.CheckAccess(long). Recommended with SimpleAccessControl/private containers."); RespectWards = config.Bind<bool>("Safety", "RespectWards", true, "Ignore containers blocked by ward/private-area access."); SkipInUseContainers = config.Bind<bool>("Safety", "SkipInUseContainers", true, "Ignore containers currently being used by another interaction."); RespectMultiUserChestIgnore = config.Bind<bool>("Safety", "RespectMultiUserChestIgnore", true, "Ignore containers marked MUC_Ignore."); DebugLogging = config.Bind<bool>("Diagnostics", "DebugLogging", false, "Enable detailed diagnostic logging."); } } internal sealed class SelectionStore { private readonly ModConfig _settings; private readonly Dictionary<string, string> _session = new Dictionary<string, string>(StringComparer.Ordinal); private readonly Dictionary<string, string> _world = new Dictionary<string, string>(StringComparer.Ordinal); private readonly string _path; private bool _loaded; private bool _dirty; internal SelectionStore(ModConfig settings) { _settings = settings; _path = Path.Combine(Paths.ConfigPath, "Zephyrfit.ManualStationPull.selections.tsv"); } internal bool TryGet(Smelter smelter, Player player, out string prefabName) { prefabName = null; string text = BuildKey(smelter, player); if (string.IsNullOrEmpty(text)) { return false; } if (_settings.SelectionPersistence.Value == SelectionPersistenceMode.Session) { return _session.TryGetValue(text, out prefabName); } EnsureLoaded(); return _world.TryGetValue(text, out prefabName); } internal void Set(Smelter smelter, Player player, string prefabName) { if (string.IsNullOrEmpty(prefabName)) { return; } string text = BuildKey(smelter, player); if (string.IsNullOrEmpty(text)) { return; } if (!_session.TryGetValue(text, out var value) || !string.Equals(value, prefabName, StringComparison.Ordinal)) { _session[text] = prefabName; } if (_settings.SelectionPersistence.Value == SelectionPersistenceMode.World) { EnsureLoaded(); if (!_world.TryGetValue(text, out var value2) || !string.Equals(value2, prefabName, StringComparison.Ordinal)) { _world[text] = prefabName; _dirty = true; Flush(); } } } internal void Flush() { if (!_dirty || !_loaded) { return; } try { Directory.CreateDirectory(Path.GetDirectoryName(_path)); using (StreamWriter streamWriter = new StreamWriter(_path, append: false)) { streamWriter.WriteLine("# ManualStationPull 0.2.x - local per-player station selections"); foreach (KeyValuePair<string, string> item in _world) { streamWriter.WriteLine(item.Key + "\t" + item.Value); } } _dirty = false; } catch (Exception ex) { ManualLogSource log = ManualStationPullPlugin.Log; if (log != null) { log.LogWarning((object)("Could not save selection store: " + ex.Message)); } } } internal unsafe static string StationCacheKey(Smelter smelter) { //IL_0036: 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) if ((Object)(object)smelter == (Object)null) { return "station:null"; } try { ZNetView obj = ((Component)smelter).GetComponent<ZNetView>() ?? ((Component)smelter).GetComponentInParent<ZNetView>(); ZDO val = ((obj != null) ? obj.GetZDO() : null); if (val != null) { ZDOID uid = val.m_uid; return "zdo:" + ((object)(*(ZDOID*)(&uid))/*cast due to .constrained prefix*/).ToString(); } } catch { } return "unity:" + ((Object)smelter).GetInstanceID(); } private string BuildKey(Smelter smelter, Player player) { long num = 0L; try { if ((Object)(object)ZNet.instance != (Object)null) { num = ZNet.instance.GetWorldUID(); } } catch { } long num2 = 0L; try { if ((Object)(object)player != (Object)null) { num2 = player.GetPlayerID(); } } catch { } return num + "|" + num2 + "|" + StationCacheKey(smelter); } private void EnsureLoaded() { if (_loaded) { return; } _loaded = true; if (!File.Exists(_path)) { return; } try { string[] array = File.ReadAllLines(_path); foreach (string text in array) { if (!string.IsNullOrWhiteSpace(text) && !text.StartsWith("#")) { int num = text.LastIndexOf('\t'); if (num > 0 && num < text.Length - 1) { string key = text.Substring(0, num); string value = text.Substring(num + 1); _world[key] = value; } } } } catch (Exception ex) { ManualLogSource log = ManualStationPullPlugin.Log; if (log != null) { log.LogWarning((object)("Could not load selection store: " + ex.Message)); } } } } internal sealed class SelectorHotkeyService { private readonly ModConfig _settings; private readonly StationInputService _stations; private static readonly FieldInfo AddOreSwitchField = AccessTools.Field(typeof(Smelter), "m_addOreSwitch"); private Smelter _hoveredSmelter; private float _lastHoverAt = -999f; internal SelectorHotkeyService(ModConfig settings, StationInputService stations) { _settings = settings; _stations = stations; } internal void MarkHovered(Smelter smelter) { if (!((Object)(object)smelter == (Object)null)) { _hoveredSmelter = smelter; _lastHoverAt = Time.unscaledTime; } } internal void Tick() { ManualStationPullPlugin instance = ManualStationPullPlugin.Instance; Player localPlayer = Player.m_localPlayer; if ((Object)(object)instance == (Object)null || (Object)(object)localPlayer == (Object)null || !_settings.Enabled.Value || (Object)(object)instance.SelectorUI == (Object)null || instance.SelectorUI.IsOpen || !ShortcutPressed()) { return; } if ((Object)(object)_hoveredSmelter == (Object)null || Time.unscaledTime - _lastHoverAt > 0.2f) { ((Character)localPlayer).Message((MessageType)2, ManualStationPullPlugin.L("Vise l'entrée de la fonderie pour choisir le minerai.", "Aim at the furnace input to select a material."), 0, (Sprite)null, false); return; } Smelter hoveredSmelter = _hoveredSmelter; if (StationInputService.IsCharcoalKiln(hoveredSmelter)) { ((Character)localPlayer).Message((MessageType)2, ManualStationPullPlugin.L("Le four à charbon utilise uniquement du bois classique.", "The charcoal kiln only uses standard Wood."), 0, (Sprite)null, false); return; } Switch val = ResolveOreSwitch(hoveredSmelter); if ((Object)(object)val == (Object)null) { ManualLogSource log = ManualStationPullPlugin.Log; if (log != null) { log.LogWarning((object)("Could not resolve m_addOreSwitch for " + ((Object)hoveredSmelter).name + ".")); } ((Character)localPlayer).Message((MessageType)2, ManualStationPullPlugin.L("Impossible d'ouvrir le sélecteur sur cette station.", "Could not open the selector for this station."), 0, (Sprite)null, false); } else { List<InputCandidate> candidates = _stations.GetCandidates(hoveredSmelter, localPlayer); if (candidates.Count == 0) { ((Character)localPlayer).Message((MessageType)2, ManualStationPullPlugin.L("Aucune ressource compatible disponible à proximité.", "No compatible input is available nearby."), 0, (Sprite)null, false); } else { instance.SelectorUI.Open(hoveredSmelter, val, localPlayer, candidates); } } } internal string GetShortcutLabel() { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: 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) string text = FriendlyKeyName(_settings.SelectorKey.Value); if ((int)_settings.SelectorModifier.Value == 0) { return text; } return FriendlyKeyName(_settings.SelectorModifier.Value) + " + " + text; } private bool ShortcutPressed() { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) KeyCode value = _settings.SelectorKey.Value; if ((int)value == 0 || !Input.GetKeyDown(value)) { return false; } KeyCode value2 = _settings.SelectorModifier.Value; if ((int)value2 == 0) { return true; } return IsHeld(value2); } private static bool IsHeld(KeyCode key) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected I4, but got Unknown //IL_0071: Unknown result type (might be due to invalid IL or missing references) switch (key - 303) { case 4: case 5: if (!Input.GetKey((KeyCode)308)) { return Input.GetKey((KeyCode)307); } return true; case 2: case 3: if (!Input.GetKey((KeyCode)306)) { return Input.GetKey((KeyCode)305); } return true; case 0: case 1: if (!Input.GetKey((KeyCode)304)) { return Input.GetKey((KeyCode)303); } return true; default: return Input.GetKey(key); } } internal unsafe static string FriendlyKeyName(KeyCode key) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected I4, but got Unknown if ((int)key != 0) { switch (key - 303) { case 4: case 5: return "Alt"; case 2: case 3: return "Ctrl"; case 0: case 1: return "Shift"; default: return ((object)(*(KeyCode*)(&key))/*cast due to .constrained prefix*/).ToString(); } } return ""; } private static Switch ResolveOreSwitch(Smelter smelter) { if ((Object)(object)smelter == (Object)null || AddOreSwitchField == null) { return null; } try { object? value = AddOreSwitchField.GetValue(smelter); return (Switch)((value is Switch) ? value : null); } catch (Exception ex) { ManualLogSource log = ManualStationPullPlugin.Log; if (log != null) { log.LogWarning((object)("Failed to resolve ore switch: " + ex.Message)); } return null; } } } internal sealed class StationInputService { private sealed class ItemStackSnapshot { internal readonly ItemData Item; internal readonly int Stack; internal ItemStackSnapshot(ItemData item, int stack) { Item = item; Stack = stack; } } private sealed class TransferTransaction { private readonly Container _sourceContainer; private readonly Inventory _sourceInventory; private readonly Inventory _playerInventory; private readonly GameObject _prefab; private bool _finished; internal ItemData StagedItem { get; } internal TransferTransaction(Container sourceContainer, Inventory sourceInventory, Inventory playerInventory, GameObject prefab, ItemData stagedItem) { _sourceContainer = sourceContainer; _sourceInventory = sourceInventory; _playerInventory = playerInventory; _prefab = prefab; StagedItem = stagedItem; } internal void Commit() { if (!_finished) { SaveContainer(_sourceContainer); _finished = true; } } internal void Rollback() { if (_finished) { return; } try { if (_playerInventory != null && StagedItem != null) { _playerInventory.RemoveItem(StagedItem, 1); } if (_sourceInventory != null && (Object)(object)_prefab != (Object)null) { _sourceInventory.AddItem(_prefab, 1); } SaveContainer(_sourceContainer); } catch (Exception ex) { ManualLogSource log = ManualStationPullPlugin.Log; if (log != null) { log.LogError((object)("Rollback failed: " + ex.Message)); } } finally { _finished = true; } } } private sealed class CountCacheEntry { internal float At; internal int Count; } private readonly ModConfig _settings; private readonly ContainerScanner _scanner; private readonly SelectionStore _selections; private readonly Dictionary<string, CountCacheEntry> _countCache = new Dictionary<string, CountCacheEntry>(StringComparer.Ordinal); private static readonly MethodInfo ContainerSave = AccessTools.Method(typeof(Container), "Save", (Type[])null, (Type[])null); private static readonly MethodInfo SmelterGetQueueSize = AccessTools.Method(typeof(Smelter), "GetQueueSize", (Type[])null, (Type[])null); private static readonly MethodInfo SmelterOnAddOre = AccessTools.Method(typeof(Smelter), "OnAddOre", new Type[3] { typeof(Switch), typeof(Humanoid), typeof(ItemData) }, (Type[])null); [ThreadStatic] private static bool _bypass; internal static bool IsBypassing => _bypass; internal StationInputService(ModConfig settings, ContainerScanner scanner, SelectionStore selections) { _settings = settings; _scanner = scanner; _selections = selections; } internal List<InputCandidate> GetCandidates(Smelter smelter, Player player) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) List<InputCandidate> list = new List<InputCandidate>(); if ((Object)(object)smelter == (Object)null || smelter.m_conversion == null || (Object)(object)player == (Object)null) { return list; } string cacheKey = SelectionStore.StationCacheKey(smelter); List<Container> eligibleContainers = _scanner.GetEligibleContainers(((Component)smelter).transform.position, player, cacheKey); HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); foreach (ItemConversion item in smelter.m_conversion) { ItemDrop val = item?.m_from; if ((Object)(object)val == (Object)null || val.m_itemData == null || val.m_itemData.m_shared == null) { continue; } string prefabName = Utils.GetPrefabName(((Component)val).gameObject); if (string.IsNullOrEmpty(prefabName) || !hashSet.Add(prefabName) || (IsCharcoalKiln(smelter) && !string.Equals(prefabName, "Wood", StringComparison.Ordinal))) { continue; } int num = CountPrefab(((Humanoid)player).GetInventory(), prefabName); int num2 = 0; foreach (Container item2 in eligibleContainers) { num2 += CountPrefab(ContainerScanner.GetLoadedInventory(item2), prefabName); } int num3 = num + num2; if (num3 > 0 || _settings.ShowUnavailableInputs.Value) { list.Add(new InputCandidate { PrefabName = prefabName, SharedName = val.m_itemData.m_shared.m_name, DisplayName = LocalizeItem(val), Icon = SafeGetIcon(val), Carried = num, Nearby = num2, Total = num3, SourceDrop = val }); } } return list.OrderByDescending((InputCandidate c) => c.Available).ThenBy<InputCandidate, string>((InputCandidate c) => c.DisplayName, StringComparer.CurrentCultureIgnoreCase).ToList(); } internal bool TryAddByPrefab(Smelter smelter, Switch inputSwitch, Player player, string prefabName) { //IL_005f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)smelter == (Object)null || (Object)(object)inputSwitch == (Object)null || (Object)(object)player == (Object)null || string.IsNullOrEmpty(prefabName)) { return false; } if (!HasQueueSpace(smelter)) { return false; } if (!IsAcceptedInput(smelter, prefabName)) { return false; } ItemData val = FindPrefab(((Humanoid)player).GetInventory(), prefabName); if (val != null) { return InvokeVanillaOre(smelter, inputSwitch, player, val); } if (!TryBeginTransaction(((Component)smelter).transform.position, player, prefabName, SelectionStore.StationCacheKey(smelter), out var transaction)) { return false; } try { if (InvokeVanillaOre(smelter, inputSwitch, player, transaction.StagedItem)) { transaction.Commit(); InvalidateCountCache(smelter, prefabName); return true; } transaction.Rollback(); return false; } catch { transaction.Rollback(); throw; } } internal int GetAvailableCountCached(Smelter smelter, Player player, string prefabName) { //IL_009c: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)smelter == (Object)null || (Object)(object)player == (Object)null || string.IsNullOrEmpty(prefabName)) { return 0; } string key = SelectionStore.StationCacheKey(smelter) + "|" + player.GetPlayerID() + "|" + prefabName; float unscaledTime = Time.unscaledTime; if (_countCache.TryGetValue(key, out var value) && unscaledTime - value.At <= 0.35f) { return value.Count; } int num = CountPrefab(((Humanoid)player).GetInventory(), prefabName); foreach (Container eligibleContainer in _scanner.GetEligibleContainers(((Component)smelter).transform.position, player, SelectionStore.StationCacheKey(smelter))) { num += CountPrefab(ContainerScanner.GetLoadedInventory(eligibleContainer), prefabName); } _countCache[key] = new CountCacheEntry { At = unscaledTime, Count = num }; return num; } internal bool IsAcceptedInput(Smelter smelter, string prefabName) { if (smelter?.m_conversion == null || string.IsNullOrEmpty(prefabName)) { return false; } foreach (ItemConversion item in smelter.m_conversion) { ItemDrop val = item?.m_from; if (!((Object)(object)val == (Object)null)) { string prefabName2 = Utils.GetPrefabName(((Component)val).gameObject); if ((!IsCharcoalKiln(smelter) || string.Equals(prefabName2, "Wood", StringComparison.Ordinal)) && string.Equals(prefabName2, prefabName, StringComparison.Ordinal)) { return true; } } } return false; } internal ItemDrop FindConversionDrop(Smelter smelter, string prefabName) { if (smelter?.m_conversion == null || string.IsNullOrEmpty(prefabName)) { return null; } foreach (ItemConversion item in smelter.m_conversion) { ItemDrop val = item?.m_from; if ((Object)(object)val != (Object)null && string.Equals(Utils.GetPrefabName(((Component)val).gameObject), prefabName, StringComparison.Ordinal)) { return val; } } return null; } internal static string LocalizeItem(ItemDrop drop) { if (drop?.m_itemData?.m_shared == null) { return "?"; } string name = drop.m_itemData.m_shared.m_name; if (Localization.instance == null) { return name; } return Localization.instance.Localize(name); } private bool TryBeginTransaction(Vector3 origin, Player player, string prefabName, string stationKey, out TransferTransaction transaction) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) transaction = null; Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory == null) { return false; } foreach (Container eligibleContainer in _scanner.GetEligibleContainers(origin, player, stationKey)) { try { ZNetView containerView = ContainerScanner.GetContainerView(eligibleContainer); if ((Object)(object)containerView == (Object)null || !containerView.IsValid()) { continue; } if (!containerView.IsOwner()) { containerView.ClaimOwnership(); } Inventory loadedInventory = ContainerScanner.GetLoadedInventory(eligibleContainer); ItemData val = FindPrefab(loadedInventory, prefabName); if (loadedInventory != null && val != null && !((Object)(object)val.m_dropPrefab == (Object)null)) { ItemData val2 = val.Clone(); val2.m_stack = 1; if (!TryInsertUniqueStagedItem(inventory, val2, prefabName, out var stagedItem)) { ((Character)player).Message((MessageType)2, ManualStationPullPlugin.L("Garde une case libre dans ton inventaire pour le transfert.", "Keep one free inventory slot for the transfer."), 0, (Sprite)null, false); return false; } int num = CountPrefab(loadedInventory, prefabName); bool num2 = loadedInventory.RemoveItem(val, 1); int num3 = CountPrefab(loadedInventory, prefabName); if (num2 && num3 < num) { transaction = new TransferTransaction(eligibleContainer, loadedInventory, inventory, val.m_dropPrefab, stagedItem); Debug("Staged 1x " + prefabName + " from " + ((Object)eligibleContainer).name + "."); return true; } if (stagedItem != null) { inventory.RemoveItem(stagedItem, 1); } } } catch (Exception ex) { ManualLogSource log = ManualStationPullPlugin.Log; if (log != null) { log.LogWarning((object)("Skipped container '" + ((eligibleContainer != null) ? ((Object)eligibleContainer).name : null) + "': " + ex.GetType().Name + ": " + ex.Message)); } } } return false; } private static bool TryInsertUniqueStagedItem(Inventory inventory, ItemData stagedTemplate, string expectedPrefabName, out ItemData stagedItem) { stagedItem = null; if (inventory == null || stagedTemplate == null || (Object)(object)stagedTemplate.m_dropPrefab == (Object)null) { return false; } try { List<ItemStackSnapshot> list = new List<ItemStackSnapshot>(); foreach (ItemData allItem in inventory.GetAllItems()) { if (MatchesPrefab(allItem, expectedPrefabName)) { list.Add(new ItemStackSnapshot(allItem, allItem.m_stack)); } } stagedTemplate.m_stack = 1; if (!inventory.AddItem(stagedTemplate)) { return false; } foreach (ItemData item in inventory.GetAllItems()) { if (!MatchesPrefab(item, expectedPrefabName)) { continue; } ItemStackSnapshot itemStackSnapshot = list.FirstOrDefault((ItemStackSnapshot snapshot) => snapshot.Item == item); if (itemStackSnapshot == null) { if (item.m_stack >= 1) { stagedItem = item; return true; } } else if (item.m_stack > itemStackSnapshot.Stack) { stagedItem = item; return true; } } ManualLogSource log = ManualStationPullPlugin.Log; if (log != null) { log.LogWarning((object)"Inventory.AddItem succeeded but the staged item could not be identified."); } return false; } catch (Exception ex) { ManualLogSource log2 = ManualStationPullPlugin.Log; if (log2 != null) { log2.LogWarning((object)("Could not stage an inventory item: " + ex.GetType().Name + ": " + ex.Message)); } return false; } } private static bool MatchesPrefab(ItemData item, string expectedPrefabName) { if (item == null || (Object)(object)item.m_dropPrefab == (Object)null || string.IsNullOrEmpty(expectedPrefabName)) { return false; } return string.Equals(Utils.GetPrefabName(item.m_dropPrefab), expectedPrefabName, StringComparison.Ordinal); } private static bool HasQueueSpace(Smelter smelter) { if ((Object)(object)smelter == (Object)null) { return false; } try { if (SmelterGetQueueSize == null) { ManualLogSource log = ManualStationPullPlugin.Log; if (log != null) { log.LogWarning((object)"Smelter.GetQueueSize reflection handle was not found."); } return true; } object obj = SmelterGetQueueSize.Invoke(smelter, null); return ((obj is int num) ? num : Convert.ToInt32(obj)) < smelter.m_maxOre; } catch (Exception ex) { ManualLogSource log2 = ManualStationPullPlugin.Log; if (log2 != null) { log2.LogWarning((object)("Could not read smelter queue size: " + ex.Message)); } return true; } } private static bool InvokeVanillaOre(Smelter smelter, Switch inputSwitch, Player player, ItemData item) { if (SmelterOnAddOre == null) { ManualLogSource log = ManualStationPullPlugin.Log; if (log != null) { log.LogError((object)"Smelter.OnAddOre reflection handle was not found."); } return false; } try { _bypass = true; object obj = SmelterOnAddOre.Invoke(smelter, new object[3] { inputSwitch, player, item }); bool flag = default(bool); int num; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } return (byte)((uint)num & (flag ? 1u : 0u)) != 0; } finally { _bypass = false; } } internal static bool IsCharcoalKiln(Smelter smelter) { if ((Object)(object)smelter == (Object)null) { return false; } string text = Utils.GetPrefabName(((Component)smelter).gameObject) ?? string.Empty; if (text.IndexOf("charcoal", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("kiln", StringComparison.OrdinalIgnoreCase) >= 0) { return true; } if (smelter.m_conversion != null) { foreach (ItemConversion item in smelter.m_conversion) { ItemDrop val = item?.m_from; if ((Object)(object)val != (Object)null && string.Equals(Utils.GetPrefabName(((Component)val).gameObject), "Wood", StringComparison.Ordinal) && (Object)(object)smelter.m_fuelItem == (Object)null) { return true; } } } return false; } internal static ItemData FindPrefab(Inventory inventory, string prefabName) { if (inventory == null || string.IsNullOrEmpty(prefabName)) { return null; } foreach (ItemData allItem in inventory.GetAllItems()) { if (!((Object)(object)allItem?.m_dropPrefab == (Object)null) && string.Equals(Utils.GetPrefabName(allItem.m_dropPrefab), prefabName, StringComparison.Ordinal)) { return allItem; } } return null; } internal static int CountPrefab(Inventory inventory, string prefabName) { if (inventory == null || string.IsNullOrEmpty(prefabName)) { return 0; } int num = 0; foreach (ItemData allItem in inventory.GetAllItems()) { if (!((Object)(object)allItem?.m_dropPrefab == (Object)null) && string.Equals(Utils.GetPrefabName(allItem.m_dropPrefab), prefabName, StringComparison.Ordinal)) { num += allItem.m_stack; } } return num; } private static Sprite SafeGetIcon(ItemDrop drop) { try { object result; if (drop == null) { result = null; } else { ItemData itemData = drop.m_itemData; result = ((itemData != null) ? itemData.GetIcon() : null); } return (Sprite)result; } catch { return null; } } private void InvalidateCountCache(Smelter smelter, string prefabName) { string prefix = SelectionStore.StationCacheKey(smelter) + "|"; foreach (string item in _countCache.Keys.Where((string k) => k.StartsWith(prefix, StringComparison.Ordinal) && k.EndsWith("|" + prefabName, StringComparison.Ordinal)).ToList()) { _countCache.Remove(item); } } private static void SaveContainer(Container container) { if ((Object)(object)container == (Object)null || ContainerSave == null) { return; } try { ContainerSave.Invoke(container, null); } catch (Exception ex) { ManualLogSource log = ManualStationPullPlugin.Log; if (log != null) { log.LogWarning((object)("Could not save container '" + ((Object)container).name + "': " + ex.Message)); } } } private static void Debug(string message) { ManualStationPullPlugin instance = ManualStationPullPlugin.Instance; if (instance != null && instance.Settings?.DebugLogging.Value == true) { ManualLogSource log = ManualStationPullPlugin.Log; if (log != null) { log.LogInfo((object)message); } } } } }