Decompiled source of SmartStorageLogistics v1.0.2
BepInEx/plugins/SmartStorageLogistics.dll
Decompiled 2 weeks 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.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text.Json; using BepInEx; using BepInEx.Configuration; using BepInEx.Core.Logging.Interpolation; using BepInEx.Logging; using BepInEx.NET.Common; using Candide; using Candide.CandideUI; using Candide.CandideUI.Components; using Candide.CandideUI.Components.Buttons; using Candide.CandideUI.Components.Displays; using Candide.CandideUI.Containers; using Candide.CandideUI.Input; using Candide.CandideUI.JobUiNew; using Candide.CandideUI.JobUiNew.State; using Candide.CandideUI.ProximityWindows; using Candide.CandideUI.Tooltip; using Candide.CandideUI.Windows; using Candide.GameModels; using Candide.GameModels.Controllers; using Candide.GameModels.Helpers; using Candide.GameModels.Managers; using Candide.GameModels.Models.Citizens; using Candide.GameModels.Models.Constructions; using Candide.GameModels.Models.Items; using Candide.GameModels.Models.Jobs; using Candide.GameModels.Services; using Candide.Graphics; using Candide.Input; using Candide.Multiplayer.Chat; using Candide.Multiplayer.Network; using Candide.Terminal; using Candide.UserSettings; using CandideCreator.Shared; using CandideCreator.Shared.Graphics; using CandideServer; using CandideServer.Buildings.BuildingControllers; using CandideServer.Data.SharedDataSetups; using CandideServer.Entities; using CandideServer.EventBus; using CandideServer.Helpers; using CandideServer.MessageModels; using CandideServer.MessageModels.Buildings; using CandideServer.MessageModels.InternalResourceStorage; using CandideServer.MessageModels.Inventories; using CandideServer.MessageModels.Jobs; using CandideServer.Models; using CandideServer.Models.Buildings; using CandideServer.Models.Logistics; using CandideServer.Models.Player; using CandideServer.Server; using CandideServer.Server.Network; using CandideServer.ServerControllers; using CandideServer.ServerManagers; using CandideServer.ServerServices; using CandideServer.ServerSystems; using CandideServer.SimulationModels; using CandideServer.Toolkit; using FontStashSharp.RichText; using HarmonyLib; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Input; using ModSettingsMenu.Api; using Shared; using Shared.Data; using Shared.Data.DataModels; using Shared.Helpers; using Shared.Inventory; using Shared.Inventory.DeltaModels; using Shared.Inventory.Models; using Shared.Models.Buildings; using Shared.Models.Construction; using Shared.Models.Items; using Shared.Text; using SmartStorageLogistics.Configuration; using SmartStorageLogistics.Features; using SmartStorageLogistics.Localizations; using SmartStorageLogistics.Networking; using SmartStorageLogistics.Ui; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("Smart Storage Logistics")] [assembly: AssemblyDescription("Smart Storage Logistics For Romestead by Ice Box Studio")] [assembly: AssemblyCompany("Ice Box Studio")] [assembly: AssemblyProduct("Smart Storage Logistics")] [assembly: AssemblyCopyright("Copyright (C) 2026 Ice Box Studio All rights reserved.")] [assembly: ComVisible(false)] [assembly: Guid("6f52443b-3b87-402a-9965-44b01b12c078")] [assembly: AssemblyFileVersion("1.0.2.0")] [assembly: TargetFramework(".NETCoreApp,Version=v8.0", FrameworkDisplayName = ".NET 8.0")] [assembly: AssemblyVersion("1.0.2.0")] [module: RefSafetyRules(11)] namespace SmartStorageLogistics { public static class PluginInfo { public const string PLUGIN_GUID = "IceBoxStudio.Romestead.SmartStorageLogistics"; public const string PLUGIN_NAME = "Smart Storage Logistics"; public const string PLUGIN_VERSION = "1.0.2"; public const string PLUGIN_AUTHOR = "Ice Box Studio"; public const string PLUGIN_AUTHOR_URL = "https://steamcommunity.com/id/ibox666/"; public const int NEXUS_MODS_ID = 47; public const string UPDATE_MANIFEST_URL = "https://fastly.jsdelivr.net/gh/ibox233/MyMod_Version@main/SmartStorageLogistics.json"; } [BepInPlugin("IceBoxStudio.Romestead.SmartStorageLogistics", "Smart Storage Logistics", "1.0.2")] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class SmartStorageLogistics : BasePlugin { private Harmony _harmony; public static SmartStorageLogistics Instance { get; private set; } internal static ManualLogSource Logger { get; private set; } public override void Load() { //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Expected O, but got Unknown //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Expected O, but got Unknown //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Expected O, but got Unknown //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Expected O, but got Unknown Instance = this; Logger = ((BasePlugin)this).Log; bool flag = default(bool); try { Logger.LogInfo((object)"============================================="); ManualLogSource logger = Logger; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(16, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>("Smart Storage Logistics"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" initializing..."); } logger.LogInfo(val); ManualLogSource logger2 = Logger; val = new BepInExInfoLogInterpolatedStringHandler(10, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Author: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>("Ice Box Studio"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("("); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>("https://steamcommunity.com/id/ibox666/"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(")"); } logger2.LogInfo(val); ConfigManager.Init(((BasePlugin)this).Config); ModSettingsMenuRegistration.Register(((BasePlugin)this).Config); BaseServer.Instance.NetworkEventBusManager.Subscribe(Assembly.GetExecutingAssembly(), (string)null); _harmony = new Harmony("IceBoxStudio.Romestead.SmartStorageLogistics"); _harmony.PatchAll(Assembly.GetExecutingAssembly()); ManualLogSource logger3 = Logger; val = new BepInExInfoLogInterpolatedStringHandler(13, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>("Smart Storage Logistics"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" initialized."); } logger3.LogInfo(val); Logger.LogInfo((object)"============================================="); } catch (Exception ex) { ManualLogSource logger4 = Logger; BepInExErrorLogInterpolatedStringHandler val2 = new BepInExErrorLogInterpolatedStringHandler(24, 3, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>("Smart Storage Logistics"); ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" initialization error: "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(ex.Message); ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("\n"); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(ex.StackTrace); } logger4.LogError(val2); } } } } namespace SmartStorageLogistics.Ui { internal static class ItemProcessingAutoFillUi { private sealed class AutoFillWindow : CandideWindow { private const int Gap = 4; private const int WidthPx = 168; private const int ContentWidth = 144; private const int EnabledLabelWidth = 88; private const int RecipeItemWidth = 128; private const int RecipeLabelWidth = 94; private readonly ItemProcessingWindow _sourceWindow; private readonly Guid _buildingId; private readonly Guid _storageInventoryId; private readonly string[] _recipeIds; private readonly CandideCheckboxLabel _enabled; private readonly CandideVerticalStackPanel _recipeList = new CandideVerticalStackPanel(); private readonly List<CandideRadioListItem> _recipeButtons = new List<CandideRadioListItem>(); private string _selectedRecipeId; private bool _loading = true; public AutoFillWindow(ItemProcessingWindow sourceWindow, ItemProcessingAutoFillResponse response) : base((WindowGraphics)1) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: 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_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Expected O, but got Unknown //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00df: 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_00f3: Expected O, but got Unknown //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Expected O, but got Unknown //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Expected O, but got Unknown _sourceWindow = sourceWindow; _buildingId = response.BuildingId; _storageInventoryId = response.StorageInventoryId; _recipeIds = response.RecipeIds ?? Array.Empty<string>(); _selectedRecipeId = (string.IsNullOrWhiteSpace(response.SelectedRecipeId) ? _recipeIds[0] : response.SelectedRecipeId); ((CandideUiElement)this).Width = 168; ((CandideUiElement)this).Padding = new CandideThickness(8); ((CandideUiElement)this).HorizontalAlignment = (HorizontalAlignment)0; ((CandideUiElement)this).VerticalAlignment = (VerticalAlignment)0; CandideVerticalStackPanel val = new CandideVerticalStackPanel { Width = 144, HorizontalAlignment = (HorizontalAlignment)1 }; ((CandideUiContainerMultipleItems)val).AddChild((CandideUiElement)new TranslationCenteredTextLayout(144, 18) { Text = Text("item_processing_autofill.title"), TextAlign = (TextHorizontalAlignment)1, HorizontalAlignment = (HorizontalAlignment)1 }, false); ((CandideUiContainerMultipleItems)val).AddChild((CandideUiElement)new CandideHorizontalSeparator { Width = 144, HorizontalAlignment = (HorizontalAlignment)1, Thickness = 1f, Margin = new CandideThickness(0, 3, 0, 6) }, false); _enabled = new CandideCheckboxLabel((CheckboxType)1, (int?)88, false) { Text = Text("item_processing_autofill.enabled"), HorizontalAlignment = (HorizontalAlignment)1 }; ((CandideTextLabel)_enabled.Label).TextAlign = (TextHorizontalAlignment)1; _enabled.Checkbox.IsToggledOn = response.Enabled; _enabled.Checkbox.Toggled += delegate { Save(); }; ((CandideUiContainerMultipleItems)val).AddChild((CandideUiElement)(object)CenterRow((CandideUiElement)(object)_enabled, 24, 0, 6), false); ((CandideUiElement)_recipeList).Width = 144; ((CandideUiElement)_recipeList).HorizontalAlignment = (HorizontalAlignment)3; ((CandideUiContainerMultipleItems)val).AddChild((CandideUiElement)(object)_recipeList, false); ((CandideUiContainerSingleItem)this).SetChild((CandideUiElement)(object)val); RebuildRecipes(); _loading = false; ((CandideWindow)_sourceWindow).Closed += OnSourceClosed; } public override void Update(GameTime gameTime) { if (((CandideUiElement)_sourceWindow).Desktop != ((CandideUiElement)this).Desktop || !((CandideUiElement)_sourceWindow).Visible) { ((CandideWindow)this).Close(); return; } _enabled.Text = Text("item_processing_autofill.enabled"); PositionToSource(); ((CandideWindow)this).Update(gameTime); } protected override void OnWindowShown() { PositionToSource(); } protected override void OnWindowClosed() { ((CandideWindow)_sourceWindow).Closed -= OnSourceClosed; if (_window == this) { _window = null; } } private void OnSourceClosed(object sender, EventArgs args) { ((CandideWindow)this).Close(); } private void RebuildRecipes() { //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_0075: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Expected O, but got Unknown ((CandideGrid)_recipeList).ClearChildren(); _recipeButtons.Clear(); string[] recipeIds = _recipeIds; foreach (string recipeId in recipeIds) { WorkRecipeModel recipeOrNull = WorkRecipeDataBase.GetRecipeOrNull(recipeId); if (recipeOrNull != null) { CandideRadioListItem item = new CandideRadioListItem(recipeOrNull.GetIcon() ?? "sam", (IconFlag)0, 94) { Text = GetRecipeName(recipeId), IsToggled = (recipeId == _selectedRecipeId), Width = 128, HorizontalAlignment = (HorizontalAlignment)1 }; ((CandideToggleButton)item).ToggledOn += delegate { _selectedRecipeId = recipeId; UpdateRecipeButtons(item); Save(); }; _recipeButtons.Add(item); ((CandideUiContainerMultipleItems)_recipeList).AddChild((CandideUiElement)(object)CenterRow((CandideUiElement)(object)item, 24, 0, 2), false); } } } private void UpdateRecipeButtons(CandideRadioListItem selected) { foreach (CandideRadioListItem recipeButton in _recipeButtons) { if (recipeButton != selected) { ((CandideToggleButton)recipeButton).IsToggled = false; } } } private static CandidePanel CenterRow(CandideUiElement child, int height, int top, int bottom) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0015: 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_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) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected O, but got Unknown CandidePanel val = new CandidePanel { Width = 144, Height = height, HorizontalAlignment = (HorizontalAlignment)3, Margin = new CandideThickness(0, top, 0, bottom) }; ((CandideUiContainerMultipleItems)val).AddChild(child, false); return val; } private void Save() { if (!_loading && !string.IsNullOrWhiteSpace(_selectedRecipeId)) { SmartStorageClient.SaveItemProcessingAutoFill(_buildingId, _storageInventoryId, _enabled.Checkbox.IsToggledOn, _selectedRecipeId); } } private void PositionToSource() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0041: 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_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) CandideDesktop desktop = ((CandideUiElement)this).Desktop; if (desktop != null) { Vector2 val = ((CandideUiElement)_sourceWindow).CalculateScreenPosition(); int num = 4 * CandideUiSystem.Scale; int num2 = (int)MathF.Round(val.X + (float)((CandideUiElement)_sourceWindow).Bounds.Width + (float)num); int value = (int)MathF.Round(val.Y); int num3 = ((CandideUiElement)desktop).LayoutBounds.Width - ((CandideUiElement)this).Bounds.Width; int val2 = ((CandideUiElement)desktop).LayoutBounds.Height - ((CandideUiElement)this).Bounds.Height; if (num2 > num3) { num2 = (int)MathF.Round(val.X - (float)((CandideUiElement)this).Bounds.Width - (float)num); } ((CandideUiElement)this).Left = Math.Clamp(num2, 0, Math.Max(0, num3)); ((CandideUiElement)this).Top = Math.Clamp(value, 0, Math.Max(0, val2)); } } private static string GetRecipeName(string recipeId) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) WorkRecipeModel recipeOrNull = WorkRecipeDataBase.GetRecipeOrNull(recipeId); if (recipeOrNull == null) { return recipeId; } string translation = StringExtensions.GetTranslation(recipeOrNull.GetName()); if (!string.IsNullOrWhiteSpace(translation)) { return translation; } return recipeId; } } private static AutoFillWindow _window; public static void AddWindow(ItemProcessingWindow sourceWindow, Guid storageInventoryId) { ItemProcessingWindow obj = sourceWindow; if (((obj != null) ? ((CandideUiElement)obj).Desktop : null) == null) { return; } AutoFillWindow window = _window; if (window != null) { ((CandideWindow)window).Close(); } SmartStorageClient.RequestItemProcessingAutoFill(storageInventoryId, delegate(ItemProcessingAutoFillResponse response) { if (((CandideUiElement)sourceWindow).Desktop != null && ((CandideUiElement)sourceWindow).Visible && response.RecipeIds != null && response.RecipeIds.Length != 0) { _window = new AutoFillWindow(sourceWindow, response); ((CandideWindow)_window).Show(((CandideUiElement)sourceWindow).Desktop, (int?)null, (int?)null); } }, delegate { }); } private static string Text(string key) { return LocalizationManager.Instance.GetText(key); } } internal static class StorageRulesUi { private sealed class StorageRulesButtonWindow : CandideWindow { private const int Gap = 2; private const int TopOffset = 31; private const int ButtonWidth = 42; private const int ButtonHeight = 20; private readonly RulesButton _button; private readonly Action _open; public InventoryWindow SourceWindow { get; } public StorageRulesButtonWindow(InventoryWindow sourceWindow, Action open) : base((WindowGraphics)0) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) SourceWindow = sourceWindow; _open = open; ((CandideUiElement)this).Width = 42; ((CandideUiElement)this).Height = 20; ((CandideUiElement)this).Padding = CandideThickness.Zero; ((CandideUiElement)this).HorizontalAlignment = (HorizontalAlignment)0; ((CandideUiElement)this).VerticalAlignment = (VerticalAlignment)0; RulesButton rulesButton = new RulesButton(); ((CandideLabelButton)rulesButton).Text = Text("storage_rules.open_button"); ((CandideUiElement)rulesButton).OnClickedSoundEvent = "event:/interface/navigate"; ((CandideButtonBase)rulesButton).OnClickAction = _open; rulesButton.GetTooltipContent = () => (IEnumerable<CandideUiElement>)(object)new CandideUiElement[1] { (CandideUiElement)new CandideTextLabel { Text = Text("storage_rules.open_tooltip") } }; rulesButton.GetPreferredTooltipGrowthDirection = () => (CandideTooltipGrowthDirection)0; _button = rulesButton; ((CandideUiContainerSingleItem)this).SetChild((CandideUiElement)(object)_button); ((CandideWindow)SourceWindow).Closed += OnSourceClosed; } public override void Update(GameTime gameTime) { if (!((CandideUiElement)SourceWindow).Visible || ((CandideUiElement)SourceWindow).Desktop != ((CandideUiElement)this).Desktop) { ((CandideWindow)this).Close(); return; } if (!SourceWindow.InventoryId.HasValue || !StorageRules.IsClientMaterialStorageInventory(SourceWindow.InventoryId.Value)) { ((CandideWindow)this).Close(); return; } ((CandideLabelButton)_button).Text = Text("storage_rules.open_button"); PositionToSource(); ((CandideWindow)this).Update(gameTime); } protected override void OnWindowClosed() { ((CandideWindow)SourceWindow).Closed -= OnSourceClosed; if (_buttonWindow == this) { _buttonWindow = null; } } protected override void OnWindowShown() { PositionToSource(); } private void OnSourceClosed(object sender, EventArgs args) { ((CandideWindow)this).Close(); } private void PositionToSource() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0081: 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) //IL_009b: Unknown result type (might be due to invalid IL or missing references) CandideDesktop desktop = ((CandideUiElement)this).Desktop; if (desktop != null) { Vector2 val = ((CandideUiElement)SourceWindow).CalculateScreenPosition(); int num = 2 * CandideUiSystem.Scale; int num2 = 31 * CandideUiSystem.Scale; int num3 = (int)MathF.Round(val.X + (float)((CandideUiElement)SourceWindow).Bounds.Width + (float)num); int value = (int)MathF.Round(val.Y + (float)num2); int num4 = ((CandideUiElement)desktop).LayoutBounds.Width - ((CandideUiElement)this).Bounds.Width; int val2 = ((CandideUiElement)desktop).LayoutBounds.Height - ((CandideUiElement)this).Bounds.Height; if (num3 > num4) { num3 = (int)MathF.Round(val.X - (float)((CandideUiElement)this).Bounds.Width - (float)num); } ((CandideUiElement)this).Left = Math.Clamp(num3, 0, Math.Max(0, num4)); ((CandideUiElement)this).Top = Math.Clamp(value, 0, Math.Max(0, val2)); } } } private sealed class RulesButton : CandideLabelButton, ITooltipContent { public Func<IEnumerable<CandideUiElement>> GetTooltipContent { get; set; } public Vector2 LocalLeftAnchorPoint => new Vector2((float)(-2 * CandideUiSystem.Scale), (float)(-2 * CandideUiSystem.Scale)); public Vector2 LocalRightAnchorPoint => new Vector2((float)(((CandideUiElement)this).InteractionBounds.Width + 2 * CandideUiSystem.Scale), (float)(-2 * CandideUiSystem.Scale)); public Func<IEnumerable<CandideUiElement>> GetComparisonTooltipContent { get; set; } public Func<CandideTooltipGrowthDirection> GetPreferredTooltipGrowthDirection { get; set; } public bool TooltipHasBeenUpdated { get; set; } public RulesButton() : base(42, (int?)18) { } } private static StorageRulesWindow _window; private static StorageRulesButtonWindow _buttonWindow; public static void AddButton(InventoryWindow window) { if (window == null) { return; } if (!window.InventoryId.HasValue || !StorageRules.IsClientMaterialStorageInventory(window.InventoryId.Value)) { CloseButton(window); } else if (_buttonWindow == null || _buttonWindow.SourceWindow != window || !((CandideUiElement)_buttonWindow).Visible) { StorageRulesButtonWindow buttonWindow = _buttonWindow; if (buttonWindow != null) { ((CandideWindow)buttonWindow).Close(); } _buttonWindow = new StorageRulesButtonWindow(window, delegate { Open(window); }); if (((CandideUiElement)window).Desktop != null) { ((CandideWindow)_buttonWindow).Show(((CandideUiElement)window).Desktop, (int?)null, (int?)null); } } } private static void Open(InventoryWindow sourceWindow) { SimpleInventory inventory = sourceWindow.Inventory; if (inventory != null && ((CandideUiElement)sourceWindow).Desktop != null) { if (_window != null && ((CandideUiElement)_window).Visible) { ((CandideWindow)_window).Close(); } _window = GameUserSettings.MouseUiAutoSettings.ApplySettings<StorageRulesWindow>(new StorageRulesWindow(inventory) { RequestBringToFront = true }, (CandideWindowDefaultPositioning)5); ((CandideWindow)_window).Show(((CandideUiElement)sourceWindow).Desktop, (int?)null, (int?)null); } } private static void CloseButton(InventoryWindow window) { if (_buttonWindow != null && _buttonWindow.SourceWindow == window) { ((CandideWindow)_buttonWindow).Close(); _buttonWindow = null; } } private static string Text(string key) { return LocalizationManager.Instance.GetText(key); } } internal sealed class StorageRulesWindow : CandideExtendedCustomGraphicsWindow { private readonly struct ItemRuleEntry { public string ItemId { get; } public string IconId { get; } public string Name { get; } public ItemRuleEntry(string itemId, string iconId, string name) { ItemId = itemId; IconId = (string.IsNullOrWhiteSpace(iconId) ? "sam" : iconId); Name = (string.IsNullOrWhiteSpace(name) ? itemId : name); } } private readonly Guid _inventoryId; private readonly SimpleInventory _inventory; private readonly CandideVerticalStackPanel _list = new CandideVerticalStackPanel(); private readonly Dictionary<string, StorageRuleMode> _modes = new Dictionary<string, StorageRuleMode>(StringComparer.Ordinal); private CandideTextLabel _loadingLabel; public StorageRulesWindow(SimpleInventory inventory) : base(new CustomBackgroundArgs { SpriteSheet = Content.SpriteSheet("media/interface/new_ui_parts/ui_chest_large"), TopLeftPixelOffset = new Point(1, 1), BottomRightPixelOffset = new Point(1, 2) }) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) _inventory = inventory; _inventoryId = inventory.Model.Id; ((CandideWindow)this).MouseSettingsId = "SmartStorageLogisticsStorageRulesWindowV4"; ((CandideUiElement)this).HorizontalAlignment = (HorizontalAlignment)0; ((CandideUiElement)this).VerticalAlignment = (VerticalAlignment)0; Build(); LoadRules(); } private void Build() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: 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_0014: Expected O, but got Unknown //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Expected O, but got Unknown //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: 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_006e: 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_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Expected O, but got Unknown //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: 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_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Expected O, but got Unknown //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_013b: 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_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Expected O, but got Unknown CandidePanel val = new CandidePanel { HorizontalAlignment = (HorizontalAlignment)3, VerticalAlignment = (VerticalAlignment)3 }; ((CandideUiContainerMultipleItems)val).AddChild((CandideUiElement)new CandideWindowAnchorDrag((CandideWindow)(object)this, new Rectangle(5, 5, 164, 12)) { Width = 164, Height = 18 }, false); ((CandideUiContainerMultipleItems)val).AddChild((CandideUiElement)new TranslationCenteredTextLayout(new Point(164, 20)) { Text = Text("storage_rules.title"), TextAlign = (TextHorizontalAlignment)1, TextStyle = (CandideTextStyle)10, TransformScale = new Vector2(0.8f), TransformOrigin = new Vector2(0.5f), Left_Pixels = 5, Top_Pixels = 1 }, false); CandideVerticalScrollViewer val2 = new CandideVerticalScrollViewer { Width = 160, Height = 326, Left_Pixels = 11, Top_Pixels = 29, HorizontalAlignment = (HorizontalAlignment)3, VerticalAlignment = (VerticalAlignment)3, Padding = CandideThickness.Zero }; ((CandideUiElement)_list).Padding = new CandideThickness(0, 0, CandideVerticalScrollViewer.ScrollbarWidth + 2, 0); ((CandideUiContainerSingleItem)val2).SetChild((CandideUiElement)(object)_list); _loadingLabel = new CandideTextLabel { Text = Text("storage_rules.loading"), Wrap = true, HorizontalAlignment = (HorizontalAlignment)3 }; ((CandideUiContainerMultipleItems)_list).AddChild((CandideUiElement)(object)_loadingLabel, false); ((CandideUiContainerMultipleItems)val).AddChild((CandideUiElement)(object)val2, false); ((CandideUiContainerSingleItem)this).SetChild((CandideUiElement)(object)val); } private void LoadRules() { SmartStorageClient.RequestStorageRules(_inventoryId, delegate(StorageRulesResponse response) { _modes.Clear(); string[] array = response.WhitelistItemIds ?? Array.Empty<string>(); foreach (string key in array) { _modes[key] = StorageRuleMode.Whitelist; } array = response.BlacklistItemIds ?? Array.Empty<string>(); foreach (string key2 in array) { _modes[key2] = StorageRuleMode.Blacklist; } RebuildList(); }, delegate { ShowError(); }); } private void ShowError() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Expected O, but got Unknown ((CandideGrid)_list).ClearChildren(); ((CandideUiContainerMultipleItems)_list).AddChild((CandideUiElement)new CandideTextLabel { Text = Text("storage_rules.load_failed"), Wrap = true, HorizontalAlignment = (HorizontalAlignment)3 }, false); } private void RebuildList() { //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_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Expected O, but got Unknown ((CandideGrid)_list).ClearChildren(); List<ItemRuleEntry> entries = GetEntries(); if (entries.Count == 0) { ((CandideUiContainerMultipleItems)_list).AddChild((CandideUiElement)new CandideTextLabel { Text = Text("storage_rules.empty"), Wrap = true, HorizontalAlignment = (HorizontalAlignment)3 }, false); return; } foreach (ItemRuleEntry item in entries) { ((CandideUiContainerMultipleItems)_list).AddChild(CreateEntryRow(item), false); } } private List<ItemRuleEntry> GetEntries() { Dictionary<string, ItemRuleEntry> dictionary = new Dictionary<string, ItemRuleEntry>(StringComparer.Ordinal); ItemInstanceModel[] inventorySlots = _inventory.Model.InventorySlots; foreach (ItemInstanceModel val in inventorySlots) { if (val != null && !string.IsNullOrWhiteSpace(val.BaseDataId)) { dictionary[val.BaseDataId] = new ItemRuleEntry(val.BaseDataId, val.Data.Icon, GetItemName(val)); } } foreach (string key in _modes.Keys) { if (!dictionary.ContainsKey(key)) { dictionary[key] = CreateEntryFromItemData(key); } } return dictionary.Values.OrderBy<ItemRuleEntry, string>((ItemRuleEntry x) => x.Name, StringComparer.CurrentCultureIgnoreCase).ToList(); } private static ItemRuleEntry CreateEntryFromItemData(string itemId) { ItemData itemDataOrMissing = ItemDataBase.GetItemDataOrMissing(itemId); return new ItemRuleEntry(itemId, itemDataOrMissing.Icon, GetItemName(itemDataOrMissing)); } private CandideUiElement CreateEntryRow(ItemRuleEntry entry) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: 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_001b: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Expected O, but got Unknown //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0083: 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_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Expected O, but got Unknown //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Expected O, but got Unknown CandideGrid val = new CandideGrid { HorizontalAlignment = (HorizontalAlignment)3, Margin = new CandideThickness(0, 0, 0, 4), ColumnsProportions = { Proportion.Auto }, ColumnsProportions = { Proportion.Fill }, ColumnsProportions = { Proportion.Auto } }; ((CandideUiContainerMultipleItems)val).AddChild((CandideUiElement)new CandideIcon((IconFlag)1) { Icon = entry.IconId, GridColumn = 0, VerticalAlignment = (VerticalAlignment)1, Margin = new CandideThickness(0, 0, 4, 0) }, false); ((CandideUiContainerMultipleItems)val).AddChild((CandideUiElement)new CandideTextLabel { Text = entry.Name, GridColumn = 1, MaxChars = 12, VerticalAlignment = (VerticalAlignment)1, HorizontalAlignment = (HorizontalAlignment)3 }, false); ((CandideUiContainerMultipleItems)val).AddChild((CandideUiElement)(object)CreateModeButton(entry.ItemId), false); return (CandideUiElement)val; } private CandideLabelButton CreateModeButton(string itemId) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Expected O, but got Unknown return new CandideLabelButton(58, (int?)18) { Text = GetModeText(GetMode(itemId)), GridColumn = 2, Margin = new CandideThickness(2, 0, 0, 0), OnClickAction = delegate { SetMode(itemId, GetNextMode(GetMode(itemId))); SaveRules(); RebuildList(); } }; } private StorageRuleMode GetMode(string itemId) { if (!_modes.TryGetValue(itemId, out var value)) { return StorageRuleMode.None; } return value; } private static StorageRuleMode GetNextMode(StorageRuleMode mode) { return mode switch { StorageRuleMode.None => StorageRuleMode.Whitelist, StorageRuleMode.Whitelist => StorageRuleMode.Blacklist, _ => StorageRuleMode.None, }; } private static string GetModeText(StorageRuleMode mode) { return mode switch { StorageRuleMode.Whitelist => Text("storage_rules.mode.whitelist"), StorageRuleMode.Blacklist => Text("storage_rules.mode.blacklist"), _ => Text("storage_rules.mode.default"), }; } private void SetMode(string itemId, StorageRuleMode mode) { if (mode == StorageRuleMode.None) { _modes.Remove(itemId); } else { _modes[itemId] = mode; } } private void SaveRules() { SmartStorageClient.SaveStorageRules(_inventoryId, from x in _modes where x.Value == StorageRuleMode.Whitelist select x.Key, from x in _modes where x.Value == StorageRuleMode.Blacklist select x.Key); } private static string GetItemName(ItemInstanceModel item) { return GetItemName(item.Data); } private static string GetItemName(ItemData data) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) if (data.NameFormatKey.HasValue) { return string.Format(StringExtensions.GetTranslation(data.NameFormatKey.Value), StringExtensions.GetTranslation(data.Name)); } return StringExtensions.GetTranslation(data.Name); } private static string Text(string key) { return LocalizationManager.Instance.GetText(key); } } internal enum StorageRuleMode { None, Whitelist, Blacklist } } namespace SmartStorageLogistics.Patches { [HarmonyPatch(typeof(CandideEngine), "Update")] internal static class CandideEngineUpdatePatch { [HarmonyPostfix] private static void Postfix() { SmartStorageClient.UpdateHotkeys(); } } [HarmonyPatch(typeof(SimpleInventoryManager), "QuickStack")] internal static class QuickStackPatch { [HarmonyPrefix] private static bool Prefix(SimpleInventory sourceInventory, SimpleInventory targetInventory) { //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0191: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_01b5: Expected O, but got Unknown if (!ConfigManager.Active || sourceInventory?.Model == null || targetInventory?.Model == null || !StorageRules.IsClientMaterialStorageInventory(targetInventory.Model.Id)) { return true; } HashSet<string> hashSet = new HashSet<string>(); bool flag = false; ItemInstanceModel[] inventorySlots = targetInventory.Model.InventorySlots; foreach (ItemInstanceModel val in inventorySlots) { if (val != null) { hashSet.Add(val.BaseDataId); if (((Enum)val.Data.Flags).HasFlag((Enum)(object)(ItemFlag)8)) { flag = true; } } } hashSet = StorageRules.GetDepositItemIds(targetInventory.Model.Id, hashSet); if (hashSet.Count == 0) { return false; } Dictionary<string, ItemAmount> dictionary = new Dictionary<string, ItemAmount>(); inventorySlots = sourceInventory.Model.InventorySlots; foreach (ItemInstanceModel val2 in inventorySlots) { if (val2 != null && hashSet.Contains(val2.BaseDataId) && !dictionary.TryAdd(val2.BaseDataId, new ItemAmount(val2.BaseDataId, val2.StackCount))) { dictionary[val2.BaseDataId] = new ItemAmount(val2.BaseDataId, dictionary[val2.BaseDataId].Amount + val2.StackCount); } } DeltaComplexResult val3 = SimpleInventoryManager.MoveItemsFromInventoryToInventory(sourceInventory, targetInventory, (IReadOnlyCollection<ItemAmount>)dictionary.Values, true, (flag && sourceInventory.Model.HasMoneyField()) ? new ulong?(sourceInventory.Model.Money.Value) : ((ulong?)null)); if ((int)val3.Result == 0 && val3.Actions.Count > 0) { SimpleInventoryService.Send_InventoryApplyManyDelta(new InventoryApplyManyDeltaMessage { Actions = val3.Actions }); } return false; } [HarmonyPostfix] private static void Postfix(SimpleInventory sourceInventory, SimpleInventory targetInventory) { if (ConfigManager.TriggerSmartDepositFromQuickStack.Value) { SmartStorageClient.SendSmartDepositNearby(); } } } [HarmonyPatch(typeof(SimpleInventoryManager), "SortInventory")] internal static class SortInventoryPatch { [HarmonyPostfix] private static void Postfix(Guid inventoryId) { if (ConfigManager.TriggerSortNearbyFromSort.Value) { SmartStorageClient.SendSortNearby(); } } } [HarmonyPatch(typeof(MillController), "Update")] internal static class MillControllerAutoFillPatch { [HarmonyPostfix] private static void Postfix(MillController __instance) { ItemProcessingAutoFill.FillBuilding(((AbstractBuildingController)__instance).Building); } } [HarmonyPatch(typeof(WaterMillController), "Update")] internal static class WaterMillControllerAutoFillPatch { [HarmonyPostfix] private static void Postfix(WaterMillController __instance) { ItemProcessingAutoFill.FillBuilding(((AbstractBuildingController)__instance).Building); } } [HarmonyPatch(typeof(DoliumController), "Update")] internal static class DoliumControllerAutoFillPatch { [HarmonyPostfix] private static void Postfix(DoliumController __instance) { ItemProcessingAutoFill.FillBuilding(((AbstractBuildingController)__instance).Building); } } [HarmonyPatch(typeof(PlayerUi), "ShowItemProcessing")] internal static class ItemProcessingAutoFillUiPatch { [HarmonyPostfix] private static void Postfix(Guid? storageInventoryId) { //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Expected O, but got Unknown if (!ConfigManager.Active || !ConfigManager.ItemProcessingAutoFillEnabled.Value || !storageInventoryId.HasValue || PlayerUi.ItemProcessingWindow == null) { return; } try { ItemProcessingAutoFillUi.AddWindow(PlayerUi.ItemProcessingWindow, storageInventoryId.Value); } catch (Exception ex) { ManualLogSource logger = SmartStorageLogistics.Logger; if (logger != null) { bool flag = default(bool); BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(44, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Failed to add item processing auto fill UI: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Exception>(ex); } logger.LogError(val); } } } } [HarmonyPatch(typeof(JobsController), "CanAffordWorkOrder")] internal static class JobsControllerCanAffordWorkOrderPatch { [HarmonyPostfix] private static void Postfix(WorkRecipeModel workRecipe, SimpleInventory characterInventory, JobInventoryStorageState inventoryStorageState, JobOrdersState jobOrdersState, JobResourceStorageState resourceStorageState, ref bool __result) { if (!__result) { __result = PlayerChestWorkOrderClient.CanAffordWithAvailableItems(workRecipe, characterInventory, inventoryStorageState, jobOrdersState, resourceStorageState); } } } [HarmonyPatch(typeof(JobsController), "MakeWorkOrder")] internal static class JobsControllerMakeWorkOrderPatch { [HarmonyPrefix] private static bool Prefix(Guid jobId, string recipeId, Guid? playerInventoryId, Guid? storageInventoryId, bool repeat, JobOrdersState jobOrdersState, JobResourceStorageState resourceStorageState, ref bool __result) { if (!PlayerChestWorkOrderClient.TryMakeWorkOrder(jobId, recipeId, playerInventoryId, storageInventoryId, repeat, jobOrdersState, resourceStorageState, out var result)) { return true; } __result = result; return false; } } [HarmonyPatch(typeof(LogisticalBuildingHelper), "SummarizeAvailableStuffForBuilding")] internal static class LogisticalBuildingHelperPatch { [HarmonyPostfix] private static void Postfix(BuildingSimulationModel building, bool checkInternalItems, ref (Dictionary<string, int>, Dictionary<string, int>) __result) { if (!checkInternalItems) { PlayerChestAutomation.AddAvailablePlayerChestItems(building, __result.Item2); } } } [HarmonyPatch(typeof(LogisticsServerManager_Requesting), "TryRequestIngredients")] internal static class LogisticsRequestingPatch { [HarmonyPostfix] private static void Postfix(BuildingInstanceModel building, ItemAmount[] itemIngredients, ResourceAmount[] resourceIngredients, int moneyCost, ref bool __result) { if (!__result) { __result = PlayerChestAutomation.TryFulfillRequestFromPlayerChests(building, itemIngredients, resourceIngredients, moneyCost); } } } [HarmonyPatch(typeof(LogisticsServerSystem), "Update")] internal static class LogisticsServerSystemPatch { [HarmonyPostfix] private static void Postfix(GameTime gt) { float dt = (float)gt.ElapsedGameTime.TotalSeconds; WellWaterLogistics.Update(); AutoLogisticsConnector.Update(); PlayerChestAutomation.Update(dt); } } [HarmonyPatch(typeof(ServerSimpleInventoryManager_Player), "Player_InventoryApplyDelta")] internal static class StorageRulesServerDeltaPatch { [HarmonyPrefix] private static bool Prefix(DeltaAction action, ConnectedPeer peer) { //IL_0007: 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_0012: Unknown result type (might be due to invalid IL or missing references) if (!ConfigManager.Active || StorageRules.AllowsManualDelta(action)) { return true; } SendDesync(action, peer); return false; } internal static void SendDesync(DeltaAction action, ConnectedPeer peer) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) HashSet<Guid> hashSet = new HashSet<Guid>(); AddInventoryId(hashSet, action.SourceInventoryId); AddInventoryId(hashSet, action.TargetInventoryId); SimpleInventoryServerService.Send_PlayerInventoriesDesynced(hashSet, peer); } internal static void AddInventoryId(HashSet<Guid> inventoryIds, Guid inventoryId) { if (inventoryId != Guid.Empty) { inventoryIds.Add(inventoryId); } } } [HarmonyPatch(typeof(ServerSimpleInventoryManager_Player), "Player_InventoryApplyManyDelta")] internal static class StorageRulesServerManyDeltaPatch { [HarmonyPrefix] private static bool Prefix(IReadOnlyCollection<DeltaAction> actions, ConnectedPeer peer) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0046: 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_0072: Unknown result type (might be due to invalid IL or missing references) if (!ConfigManager.Active || actions == null || actions.All(StorageRules.AllowsManualDelta)) { return true; } HashSet<Guid> hashSet = new HashSet<Guid>(); foreach (DeltaAction action in actions) { StorageRulesServerDeltaPatch.AddInventoryId(hashSet, action.SourceInventoryId); StorageRulesServerDeltaPatch.AddInventoryId(hashSet, action.TargetInventoryId); } SimpleInventoryServerService.Send_PlayerInventoriesDesynced(hashSet, peer); return false; } } [HarmonyPatch(typeof(ServerSimpleInventoryManager_Player), "Player_LootAll")] internal static class StorageRulesServerLootAllPatch { [HarmonyPrefix] private static bool Prefix(Guid sourceInventoryId, Guid targetInventoryId, Guid? equipmentInventoryId, bool allowPartialStack, LootAllStrategy strategy, ConnectedPeer peer) { //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) if (!ConfigManager.Active || !StorageRules.IsMaterialStorageInventory(targetInventoryId)) { return true; } SharedInventoryModel val = default(SharedInventoryModel); SharedInventoryModel val2 = default(SharedInventoryModel); if (!ServerGameState.TryGetInventory(sourceInventoryId, ref val) || !ServerGameState.TryGetInventory(targetInventoryId, ref val2)) { return false; } SharedInventoryModel val3 = default(SharedInventoryModel); if (equipmentInventoryId.HasValue && !ServerGameState.TryGetInventory(equipmentInventoryId.Value, ref val3)) { return false; } List<ItemAmount> list = FilterItemsForMaterialStorage(val, val2); if (list.Count == 0) { SimpleInventoryServerService.Send_PlayerInventoriesDesynced(new HashSet<Guid> { sourceInventoryId, targetInventoryId }, peer); return false; } DeltaComplexResult val4 = ComplexInventoryDeltaEngine.MoveItemAmountsFromInventoryToInventory((IReadOnlyCollection<ItemAmount>)list, val, val2, (ulong?)null, allowPartialStack); if ((int)val4.Result == 0 && val4.Actions.Count > 0) { ServerSimpleInventoryManager_Player.Player_InventoryApplyManyDelta((IReadOnlyCollection<DeltaAction>)val4.Actions, peer); } return false; } private static List<ItemAmount> FilterItemsForMaterialStorage(SharedInventoryModel sourceInventory, SharedInventoryModel targetInventory) { //IL_00bb: Unknown result type (might be due to invalid IL or missing references) Dictionary<string, int> dictionary = new Dictionary<string, int>(StringComparer.Ordinal); ItemInstanceModel[] inventorySlots = sourceInventory.InventorySlots; foreach (ItemInstanceModel val in inventorySlots) { if (val != null && val.StackCount > 0 && targetInventory.FilterAcceptsItem(val) && StorageRules.AllowsItem(targetInventory.Id, val.BaseDataId) && !dictionary.TryAdd(val.BaseDataId, val.StackCount)) { dictionary[val.BaseDataId] += val.StackCount; } } List<ItemAmount> list = new List<ItemAmount>(dictionary.Count); foreach (KeyValuePair<string, int> item in dictionary) { list.Add(new ItemAmount(item.Key, item.Value)); } return list; } } [HarmonyPatch(typeof(SimpleInventoryManager), "AddItemFromSourceAction")] internal static class StorageRulesAddItemFromSourcePatch { [HarmonyPrefix] private static bool Prefix(SimpleInventory sourceInventory, int sourceIndex, SimpleInventory targetInventory, ref bool __result) { if (!ConfigManager.Active || sourceInventory?.Model == null || targetInventory?.Model == null || sourceInventory.Model.Id == targetInventory.Model.Id || sourceIndex < 0 || sourceIndex >= sourceInventory.Model.InventorySlots.Length) { return true; } if (StorageRules.AllowsManualClientMove(targetInventory.Model.Id, sourceInventory.Model.InventorySlots[sourceIndex])) { return true; } __result = false; return false; } } [HarmonyPatch(typeof(SimpleInventoryManager), "ItemSwapAction")] internal static class StorageRulesItemSwapPatch { [HarmonyPrefix] private static bool Prefix(Guid sourceInventoryId, int sourceSlotIndex, Guid targetInventoryId, int targetSlotIndex, ref bool __result) { if (!ConfigManager.Active || sourceInventoryId == targetInventoryId) { return true; } if (StorageRules.AllowsManualClientSwap(sourceInventoryId, sourceSlotIndex, targetInventoryId, targetSlotIndex)) { return true; } __result = false; return false; } } [HarmonyPatch(typeof(SimpleInventoryManager), "SplitStack")] internal static class StorageRulesSplitStackPatch { [HarmonyPrefix] private static bool Prefix(Guid sourceInventoryId, Guid targetInventoryId, int splitAmount, int sourceIndex, int targetIndex) { SimpleInventory val = default(SimpleInventory); if (!ConfigManager.Active || sourceInventoryId == targetInventoryId || !GameState.TryGetActiveInventory(sourceInventoryId, ref val)) { return true; } if (sourceIndex < 0 || sourceIndex >= val.Model.InventorySlots.Length) { return true; } return StorageRules.AllowsManualClientMove(targetInventoryId, val.Model.InventorySlots[sourceIndex]); } } [HarmonyPatch(typeof(SimpleInventoryManager), "MoneySwapAction")] internal static class StorageRulesMoneySwapPatch { [HarmonyPrefix] private static bool Prefix(Guid sourceInventoryId, ulong coinAmount, int coinType, Guid targetInventoryId, int slotIndex, bool allowPartialStack, ref bool __result) { if (!ConfigManager.Active || sourceInventoryId == targetInventoryId || coinAmount == 0L) { return true; } if (StorageRules.AllowsManualClientMoneyMove(targetInventoryId, coinType)) { return true; } __result = false; return false; } } [HarmonyPatch] internal static class StorageRulesLogisticsPatch { private static MethodBase TargetMethod() { return AccessTools.Method(typeof(LogisticsServerManager_Repeating), "LootAllThroughOutputConnections", new Type[4] { typeof(List<Guid>), typeof(SharedInventoryModel), typeof(bool), typeof(Guid?) }, (Type[])null); } private static bool Prefix(List<Guid> outputConnectionIds, SharedInventoryModel sourceInventory, bool allowPartialStack, Guid? sourceBuildingIdCheck, ref DeltaComplexResult __result) { //IL_0024: 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) if (!ConfigManager.Active || outputConnectionIds == null || sourceInventory == null) { return true; } bool[] targetUsesRule; SharedInventoryModel[] inventories = BuildInventories(outputConnectionIds, sourceInventory, sourceBuildingIdCheck, out targetUsesRule); if (targetUsesRule == null) { return true; } __result = LootAllIntoAllowedInventories(inventories, targetUsesRule, allowPartialStack); return false; } private static SharedInventoryModel[] BuildInventories(List<Guid> outputConnectionIds, SharedInventoryModel sourceInventory, Guid? sourceBuildingIdCheck, out bool[] targetUsesRule) { List<SharedInventoryModel> list = new List<SharedInventoryModel> { sourceInventory }; List<bool> list2 = new List<bool> { false }; bool flag = false; LogisticsConnectionModel val = default(LogisticsConnectionModel); BuildingSimulationModel val2 = default(BuildingSimulationModel); SharedInventoryModel val3 = default(SharedInventoryModel); foreach (Guid outputConnectionId in outputConnectionIds) { if (ServerGameState.TryGetLogisticsConnection(outputConnectionId, ref val) && val.Config.AcceptsRepeatingItems && ServerGameState.TryGetBuilding(val.DestinationBuildingId, ref val2)) { if (val2.InstanceModel.Id == sourceBuildingIdCheck) { throw new Exception("Call Samuel about Items Logistics Connection"); } Guid? storageInventoryId = val2.InstanceModel.StorageInventoryId; if (storageInventoryId.HasValue && ServerGameState.TryGetInventory(storageInventoryId.Value, ref val3)) { list.Add(val3); bool flag2 = StorageRules.HasMaterialStorageRule(val3.Id); list2.Add(flag2); flag = flag || flag2; } } } targetUsesRule = (flag ? list2.ToArray() : null); return list.ToArray(); } private static DeltaComplexResult LootAllIntoAllowedInventories(SharedInventoryModel[] inventories, bool[] targetUsesRule, bool allowPartialStack) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected O, but got Unknown //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0116: 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_00d4: 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_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) DeltaTransaction val = new DeltaTransaction(inventories); bool flag = false; bool flag2 = false; bool flag3 = false; for (int i = 0; i < val.Inventories[0].InventorySlots.Length; i++) { ItemInstanceModel val2 = val.Inventories[0].InventorySlots[i]; if (val2 == null) { flag = true; continue; } bool flag4 = false; bool flag5 = false; for (int j = 1; j < val.Length; j++) { if (targetUsesRule[j] && !StorageRules.AllowsItem(val.Inventories[j].Id, val2.BaseDataId)) { flag3 = true; continue; } flag4 = true; if (val.LootItem(0, i, allowPartialStack, j)) { flag5 = true; flag2 = true; break; } val2 = val.Inventories[0].InventorySlots[i]; if (val2 == null) { flag5 = true; flag2 = true; break; } } if (!flag5 && !allowPartialStack) { if (!flag4) { return new DeltaComplexResult { Actions = val.Actions, Result = (InventoryResult)4 }; } return val.Build(); } flag = true; } DeltaComplexResult result = val.Build(); if (flag && allowPartialStack) { result.Result = (InventoryResult)((!flag2 && flag3) ? 4 : 0); } return result; } } [HarmonyPatch(typeof(ServerSimpleInventoryManager_Server), "Server_InventoryApplyManyDelta")] internal static class StorageRulesServerLogisticsManyDeltaPatch { private static bool Prefix(IReadOnlyCollection<DeltaAction> actions) { if (ConfigManager.Active && actions != null && actions.Count != 0) { return AllLogisticsDepositsAllowed(actions); } return true; } private static bool AllLogisticsDepositsAllowed(IReadOnlyCollection<DeltaAction> actions) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) foreach (DeltaAction action in actions) { if (!StorageRules.AllowsLogisticsDepositDelta(action)) { return false; } } return true; } } [HarmonyPatch(typeof(PlayerUi), "ShowItemStorage")] internal static class StorageRulesUiPatch { [HarmonyPostfix] private static void Postfix(bool gravestoneUi) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Expected O, but got Unknown if (!ConfigManager.Active || gravestoneUi) { return; } try { StorageRulesUi.AddButton(PlayerUi.SecondaryInventoryWindow); } catch (Exception ex) { ManualLogSource logger = SmartStorageLogistics.Logger; if (logger != null) { bool flag = default(bool); BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(36, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Failed to add storage rules button: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Exception>(ex); } logger.LogError(val); } } } } [HarmonyPatch(typeof(SharedDataSetup), "SharedSetup")] internal static class WellWaterLogisticsDataPatch { [HarmonyPostfix] private static void Postfix() { WellWaterLogistics.PatchBuildingData(); } } [HarmonyPatch(typeof(ConcreteMixerController), "Update")] internal static class ConcreteMixerWaterRequestPatch { private static readonly ResourceAmount[] WaterAmount = (ResourceAmount[])(object)new ResourceAmount[1] { new ResourceAmount("resource:water", 1) }; [HarmonyPostfix] private static void Postfix(ConcreteMixerController __instance) { if (ConfigManager.Active && ConfigManager.WellWaterLogisticsEnabled.Value && ((AbstractBuildingController)(__instance?)).Building?.InstanceModel != null && ((AbstractBuildingController)__instance).Timer == 0f) { InternalResourceStorageModel internalResourceStorageModel = ((AbstractBuildingController)__instance).GetInternalResourceStorageModel(); if (internalResourceStorageModel != null && internalResourceStorageModel.GetResourceAmount("resource:water") <= 0) { LogisticsServerManager_Requesting.TryRequestIngredients(((AbstractBuildingController)__instance).Building.InstanceModel, Array.Empty<ItemAmount>(), WaterAmount, 0); } } } } [HarmonyPatch(typeof(BuildingsController), "SyncFullGameState")] internal static class WellWaterLogisticsClientFullSyncPatch { [HarmonyPostfix] private static void Postfix() { WellWaterLogistics.PatchClientBuildings(); } } [HarmonyPatch(typeof(BuildingsController), "BuildingSpawned")] internal static class WellWaterLogisticsClientSpawnPatch { [HarmonyPostfix] private static void Postfix(BuildingInstanceModel instanceModel) { WellWaterLogistics.PatchWellInstance(instanceModel); } } [HarmonyPatch(typeof(WorkRecipeItemIngredientDisplay), "InvalidateData")] internal static class WorkRecipeItemIngredientDisplayPatch { [HarmonyPostfix] private static void Postfix(WorkRecipeItemIngredientDisplay __instance) { //IL_0001: 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_0007: Unknown result type (might be due to invalid IL or missing references) //IL_003e: 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_0066: 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) ItemAmount itemIngredient = __instance.ItemIngredient; if (PlayerChestWorkOrderClient.TryGetDisplayedItemAmount(itemIngredient, __instance.CharacterInventoryId, __instance.StorageInventoryId, out var amount)) { ((HolderIconLabel)__instance).Text = $"{amount}/{itemIngredient.Amount}"; ((HolderIconLabel)__instance).TextColor = ((amount >= itemIngredient.Amount) ? StyleHelper.TextNormalColor : StyleHelper.TextWarningColor); } } } } namespace SmartStorageLogistics.Networking { internal static class ClientNetwork { private delegate void SendMessage3(string key, object message, byte type); private delegate void SendMessage5(string key, object message, byte type, bool reliable, bool ordered); private static readonly SendMessage3 Send3; private static readonly SendMessage5 Send5; private static bool _loggedMissingSendMessage; static ClientNetwork() { BindingFlags bindingAttr = BindingFlags.Static | BindingFlags.Public; Type typeFromHandle = typeof(NetworkManager); MethodInfo method = typeFromHandle.GetMethod("SendMessage", bindingAttr, null, new Type[5] { typeof(string), typeof(object), typeof(byte), typeof(bool), typeof(bool) }, null); if (method != null) { Send5 = (SendMessage5)Delegate.CreateDelegate(typeof(SendMessage5), method); return; } MethodInfo method2 = typeFromHandle.GetMethod("SendMessage", bindingAttr, null, new Type[3] { typeof(string), typeof(object), typeof(byte) }, null); if (method2 != null) { Send3 = (SendMessage3)Delegate.CreateDelegate(typeof(SendMessage3), method2); } } public static void SendMessage(string key, object message, byte type = 2, bool reliable = true, bool ordered = false) { if (Send5 != null) { Send5(key, message, type, reliable, ordered); } else if (Send3 != null) { Send3(key, message, type); } else if (!_loggedMissingSendMessage) { _loggedMissingSendMessage = true; ManualLogSource logger = SmartStorageLogistics.Logger; if (logger != null) { logger.LogError((object)"NetworkManager.SendMessage signature not found."); } } } } internal static class SmartStorageEventKeys { public const string SmartDepositNearby = "smart-storage-logistics-smart-deposit-nearby"; public const string SortNearby = "smart-storage-logistics-sort-nearby"; public const string GetStorageRules = "smart-storage-logistics-get-storage-rules"; public const string SetStorageRules = "smart-storage-logistics-set-storage-rules"; public const string GetItemProcessingAutoFill = "smart-storage-logistics-get-item-processing-auto-fill"; public const string SetItemProcessingAutoFill = "smart-storage-logistics-set-item-processing-auto-fill"; public const string OperationSucceeded = "smart-storage-logistics-operation-succeeded"; public const string OperationSmartDeposit = "smart-deposit"; public const string OperationSortNearby = "sort-nearby"; } public sealed class StorageRulesRequest { public Guid InventoryId; } public sealed class StorageRulesResponse { public Guid InventoryId; public string[] WhitelistItemIds = Array.Empty<string>(); public string[] BlacklistItemIds = Array.Empty<string>(); } public sealed class StorageRulesSaveMessage { public Guid InventoryId; public string[] WhitelistItemIds = Array.Empty<string>(); public string[] BlacklistItemIds = Array.Empty<string>(); } public sealed class ItemProcessingAutoFillRequest { public Guid StorageInventoryId; } public sealed class ItemProcessingAutoFillResponse { public Guid BuildingId; public Guid StorageInventoryId; public string BuildingTypeId; public bool Enabled; public string SelectedRecipeId; public string[] RecipeIds = Array.Empty<string>(); } public sealed class ItemProcessingAutoFillSaveMessage { public Guid BuildingId; public Guid StorageInventoryId; public bool Enabled; public string SelectedRecipeId; } } namespace SmartStorageLogistics.Localizations { internal static class LocalizationHelper { public static readonly string[] SupportedLanguages = new string[4] { "zh_CN", "zh_TW", "en", "ja_JP" }; public static Dictionary<string, string> GetDefaultTranslations(string language) { Dictionary<string, string> dictionary = new Dictionary<string, string>(); switch (language) { case "zh_CN": dictionary.Add("mod.name", "智能仓储物流"); dictionary.Add("mod.description", "为背包、附近箱子、建筑仓储和粮仓增加更省心的整理、入库和自动化存取逻辑。"); dictionary.Add("section.general", "通用"); dictionary.Add("section.storage", "仓储"); dictionary.Add("section.logistics", "物流"); dictionary.Add("section.controls", "操作"); dictionary.Add("entry.enabled", "启用模组"); dictionary.Add("entry.smart_deposit", "附近智能入库"); dictionary.Add("entry.sort_nearby", "附近一键整理"); dictionary.Add("entry.sort_player_inventory", "整理玩家背包"); dictionary.Add("entry.sort_nearby_chests", "整理附近仓储"); dictionary.Add("entry.nearby_radius", "附近范围"); dictionary.Add("entry.material_storage_reserved_slots", "材料仓库预留空格"); dictionary.Add("entry.auto_connect", "材料仓库自动供工坊"); dictionary.Add("entry.well_water_logistics", "水井供水物流"); dictionary.Add("entry.automation_player_chests", "玩家箱子自动化总开关"); dictionary.Add("entry.pull_ingredients_from_player_chests", "从玩家箱子自动取料"); dictionary.Add("entry.deposit_outputs_to_player_chests", "自动存入玩家箱子"); dictionary.Add("entry.item_processing_autofill", "加工建筑自动补料"); dictionary.Add("entry.player_chest_automation_radius", "工坊箱子取料范围"); dictionary.Add("entry.trigger_quick_stack", "快速合并时智能入库"); dictionary.Add("entry.trigger_sort", "整理时触发附近整理"); dictionary.Add("entry.smart_deposit_hotkey", "智能入库热键"); dictionary.Add("entry.sort_nearby_hotkey", "附近整理热键"); dictionary.Add("config.enabled.description", "智能仓储物流的总开关。"); dictionary.Add("config.smart_deposit.description", "把玩家背包中能匹配到附近箱子、建筑仓储或粮仓已有同类物品的内容堆入对应库存。"); dictionary.Add("config.sort_nearby.description", "允许服务端整理玩家背包、附近箱子、建筑仓储和粮仓。"); dictionary.Add("config.sort_player_inventory.description", "执行附近整理时同时整理玩家背包。"); dictionary.Add("config.sort_nearby_chests.description", "执行附近整理时整理附近箱子、建筑仓储和粮仓。"); dictionary.Add("config.nearby_radius.description", "玩家附近可整理仓储的搜索范围,单位为游戏世界距离。16 约等于 1 格。"); dictionary.Add("config.material_storage_reserved_slots.description", "附近智能入库向材料仓库搬入物品时保留的空格数量。材料仓库只会接收已在该仓库中存在同类物品的内容。"); dictionary.Add("config.auto_connect.description", "默认关闭。开启后载入建筑时自动执行一次,为同城工坊创建材料仓库到工坊的请求型物流连接。"); dictionary.Add("config.well_water_logistics.description", "让水井作为水资源输出源参与物流,并允许混凝土搅拌机缺水时通过物流自动取水。"); dictionary.Add("config.automation_player_chests.description", "控制工坊和加工建筑是否允许使用附近玩家放置的箱子。关闭后自动取料和自动存入都会停止。"); dictionary.Add("config.pull_ingredients_from_player_chests.description", "允许工坊和带加工窗口的建筑从附近玩家箱子取用配方所需物品。"); dictionary.Add("config.deposit_outputs_to_player_chests.description", "把建筑输出合并到附近已有同类物品的玩家箱子。默认关闭,避免产物从建筑输出库存中自动移走。"); dictionary.Add("config.item_processing_autofill.description", "允许手动磨坊、水磨和酒缸等带加工窗口的建筑按玩家选择的配方从附近玩家箱子自动补料。"); dictionary.Add("config.player_chest_automation_radius.description", "工坊从大世界附近玩家箱子取用物品材料的搜索范围,单位为游戏世界距离。16 约等于 1 格。"); dictionary.Add("config.trigger_quick_stack.description", "使用原版快速合并时额外执行附近智能入库。"); dictionary.Add("config.trigger_sort.description", "使用原版整理时额外执行附近一键整理。"); dictionary.Add("config.smart_deposit_hotkey.description", "按下该按键时执行附近智能入库。默认:F7。"); dictionary.Add("config.sort_nearby_hotkey.description", "按下该按键时执行附近一键整理。默认:F8。"); dictionary.Add("status.smart_deposit.success", "智能入库完成!"); dictionary.Add("status.sort_nearby.success", "附近整理完成!"); dictionary.Add("storage_rules.title", "材料仓库规则"); dictionary.Add("storage_rules.loading", "正在读取仓储规则..."); dictionary.Add("storage_rules.footer", "规则影响自动入库、手动放入和物流放入材料仓库,不影响取出或供工坊取料。设置了任何允许物品时,只接收允许物品;阻止物品始终拒绝。"); dictionary.Add("storage_rules.load_failed", "无法读取该材料仓库规则。请确认材料仓库仍在可访问范围内。"); dictionary.Add("storage_rules.empty", "当前材料仓库没有可配置的物品。先放入一个物品,再设置规则。"); dictionary.Add("storage_rules.mode.default", "默认"); dictionary.Add("storage_rules.mode.whitelist", "允许"); dictionary.Add("storage_rules.mode.blacklist", "阻止"); dictionary.Add("storage_rules.open_button", "规则"); dictionary.Add("storage_rules.open_tooltip", "打开材料仓库规则"); dictionary.Add("item_processing_autofill.title", "自动补料"); dictionary.Add("item_processing_autofill.enabled", "从附近箱子补料"); break; case "zh_TW": dictionary.Add("mod.name", "智慧倉儲物流"); dictionary.Add("mod.description", "為背包、附近箱子、建築倉儲和糧倉加入更省心的整理、入庫和自動化存取邏輯。"); dictionary.Add("section.general", "通用"); dictionary.Add("section.storage", "倉儲"); dictionary.Add("section.logistics", "物流"); dictionary.Add("section.controls", "操作"); dictionary.Add("entry.enabled", "啟用模組"); dictionary.Add("entry.smart_deposit", "附近智慧入庫"); dictionary.Add("entry.sort_nearby", "附近一鍵整理"); dictionary.Add("entry.sort_player_inventory", "整理玩家背包"); dictionary.Add("entry.sort_nearby_chests", "整理附近倉儲"); dictionary.Add("entry.nearby_radius", "附近範圍"); dictionary.Add("entry.material_storage_reserved_slots", "材料倉庫預留空格"); dictionary.Add("entry.auto_connect", "材料倉庫自動供工坊"); dictionary.Add("entry.well_water_logistics", "水井供水物流"); dictionary.Add("entry.automation_player_chests", "玩家箱子自動化總開關"); dictionary.Add("entry.pull_ingredients_from_player_chests", "從玩家箱子自動取料"); dictionary.Add("entry.deposit_outputs_to_player_chests", "自動存入玩家箱子"); dictionary.Add("entry.item_processing_autofill", "加工建築自動補料"); dictionary.Add("entry.player_chest_automation_radius", "工坊箱子取料範圍"); dictionary.Add("entry.trigger_quick_stack", "快速合併時智慧入庫"); dictionary.Add("entry.trigger_sort", "整理時觸發附近整理"); dictionary.Add("entry.smart_deposit_hotkey", "智慧入庫熱鍵"); dictionary.Add("entry.sort_nearby_hotkey", "附近整理熱鍵"); dictionary.Add("config.enabled.description", "智慧倉儲物流的總開關。"); dictionary.Add("config.smart_deposit.description", "把玩家背包中能匹配到附近箱子、建築倉儲或糧倉已有同類物品的內容堆入對應庫存。"); dictionary.Add("config.sort_nearby.description", "允許服務端整理玩家背包、附近箱子、建築倉儲和糧倉。"); dictionary.Add("config.sort_player_inventory.description", "執行附近整理時同時整理玩家背包。"); dictionary.Add("config.sort_nearby_chests.description", "執行附近整理時整理附近箱子、建築倉儲和糧倉。"); dictionary.Add("config.nearby_radius.description", "玩家附近可整理倉儲的搜尋範圍,單位為遊戲世界距離。16 約等於 1 格。"); dictionary.Add("config.material_storage_reserved_slots.description", "附近智慧入庫向材料倉庫搬入物品時保留的空格數量。材料倉庫只會接收已在該倉庫中存在同類物品的內容。"); dictionary.Add("config.auto_connect.description", "預設關閉。開啟後載入建築時自動執行一次,為同城工坊建立材料倉庫到工坊的請求型物流連接。"); dictionary.Add("config.well_water_logistics.description", "讓水井作為水資源輸出源參與物流,並允許混凝土攪拌機缺水時透過物流自動取水。"); dictionary.Add("config.automation_player_chests.description", "控制工坊和加工建築是否允許使用附近玩家放置的箱子。關閉後自動取料和自動存入都會停止。"); dictionary.Add("config.pull_ingredients_from_player_chests.description", "允許工坊和帶加工視窗的建築從附近玩家箱子取用配方所需物品。"); dictionary.Add("config.deposit_outputs_to_player_chests.description", "把建築輸出合併到附近已有同類物品的玩家箱子。預設關閉,避免產物從建築輸出庫存中自動移走。"); dictionary.Add("config.item_processing_autofill.description", "允許手動磨坊、水磨和酒缸等帶加工視窗的建築按玩家選擇的配方從附近玩家箱子自動補料。"); dictionary.Add("config.player_chest_automation_radius.description", "工坊從大世界附近玩家箱子取用物品材料的搜尋範圍,單位為遊戲世界距離。16 約等於 1 格。"); dictionary.Add("config.trigger_quick_stack.description", "使用原版快速合併時額外執行附近智慧入庫。"); dictionary.Add("config.trigger_sort.description", "使用原版整理時額外執行附近一鍵整理。"); dictionary.Add("config.smart_deposit_hotkey.description", "按下該按鍵時執行附近智慧入庫。預設:F7。"); dictionary.Add("config.sort_nearby_hotkey.description", "按下該按鍵時執行附近一鍵整理。預設:F8。"); dictionary.Add("status.smart_deposit.success", "智慧入庫完成!"); dictionary.Add("status.sort_nearby.success", "附近整理完成!"); dictionary.Add("storage_rules.title", "材料倉庫規則"); dictionary.Add("storage_rules.loading", "正在讀取倉儲規則..."); dictionary.Add("storage_rules.footer", "規則影響自動入庫、手動放入和物流放入材料倉庫,不影響取出或供工坊取料。設定了任何允許物品時,只接收允許物品;阻止物品始終拒絕。"); dictionary.Add("storage_rules.load_failed", "無法讀取該材料倉庫規則。請確認材料倉庫仍在可存取範圍內。"); dictionary.Add("storage_rules.empty", "目前材料倉庫沒有可配置的物品。先放入一個物品,再設定規則。"); dictionary.Add("storage_rules.mode.default", "預設"); dictionary.Add("storage_rules.mode.whitelist", "允許"); dictionary.Add("storage_rules.mode.blacklist", "阻止"); dictionary.Add("storage_rules.open_button", "規則"); dictionary.Add("storage_rules.open_tooltip", "開啟材料倉庫規則"); dictionary.Add("item_processing_autofill.title", "自動補料"); dictionary.Add("item_processing_autofill.enabled", "從附近箱子補料"); break; case "ja_JP": dictionary.Add("mod.name", "Smart Storage Logistics"); dictionary.Add("mod.description", "バックパック、近くの箱、建物倉庫、食料庫の整理、入庫、自動化アクセスを改善します。"); dictionary.Add("section.general", "全般"); dictionary.Add("section.storage", "倉庫"); dictionary.Add("section.logistics", "物流"); dictionary.Add("section.controls", "操作"); dictionary.Add("entry.enabled", "MODを有効化"); dictionary.Add("entry.smart_deposit", "近くへスマート入庫"); dictionary.Add("entry.sort_nearby", "近くを整理"); dictionary.Add("entry.sort_player_inventory", "プレイヤー所持品を整理"); dictionary.Add("entry.sort_nearby_chests", "近くの倉庫を整理"); dictionary.Add("entry.nearby_radius", "近くの範囲"); dictionary.Add("entry.material_storage_reserved_slots", "材料倉庫の空き枠予約"); dictionary.Add("entry.auto_connect", "工房へ自動供給"); dictionary.Add("entry.well_water_logistics", "井戸の水物流"); dictionary.Add("entry.automation_player_chests", "プレイヤー箱自動化の主スイッチ"); dictionary.Add("entry.pull_ingredients_from_player_chests", "プレイヤー箱から自動取得"); dictionary.Add("entry.deposit_outputs_to_player_chests", "プレイヤー箱へ自動保管"); dictionary.Add("entry.item_processing_autofill", "加工建物の自動補充"); dictionary.Add("entry.player_chest_automation_radius", "工房の箱検索範囲"); dictionary.Add("entry.trigger_quick_stack", "クイックスタック時に実行"); dictionary.Add("entry.trigger_sort", "整理時に実行"); dictionary.Add("entry.smart_deposit_hotkey", "スマート入庫ホットキー"); dictionary.Add("entry.sort_nearby_hotkey", "近くを整理ホットキー"); dictionary.Add("config.enabled.description", "Smart Storage Logistics のマスタースイッチ。"); dictionary.Add("config.smart_deposit.description", "プレイヤー所持品のうち、近くの箱、建物倉庫、食料庫に同じ種類が入っているアイテムを対応する在庫へ移します。"); dictionary.Add("config.sort_nearby.description", "プレイヤー所持品、近くの箱、建物倉庫、食料庫をサーバー側で整理できるようにします。"); dictionary.Add("config.sort_player_inventory.description", "近くの整理を実行するとき、プレイヤー所持品も整理します。"); dictionary.Add("config.sort_nearby_chests.description", "近くの整理を実行するとき、近くの箱、建物倉庫、食料庫を整理します。"); dictionary.Add("config.nearby_radius.description", "整理対象となる近くの倉庫の検索範囲。単位はゲーム内ワールド距離です。16 はおよそ 1 タイルです。"); dictionary.Add("config.material_storage_reserved_slots.description", "近くへスマート入庫で材料倉庫にアイテムを入れるとき、空けておくスロット数。材料倉庫は、その倉庫内に既に存在する種類のアイテムだけを受け取ります。"); dictionary.Add("config.auto_connect.description", "既定ではオフです。有効にすると建物の読み込み時に一度だけ、同じ町の工房へ材料倉庫からの要求型物流接続を自動作成します。"); dictionary.Add("config.well_water_logistics.description", "井戸を水資源の物流出力元として使えるようにし、コンクリートミキサーが水不足のとき物流で自動取得できるようにします。"); dictionary.Add("config.automation_player_chests.description", "工房と加工建物が近くのプレイヤー設置箱を使えるかを制御します。オフにすると自動取得と自動保管の両方が停止します。"); dictionary.Add("config.pull_ingredients_from_player_chests.description", "工房と加工ウィンドウを持つ建物が、近くのプレイヤー箱からレシピに必要なアイテムを取れるようにします。"); dictionary.Add("config.deposit_outputs_to_player_chests.description", "建物の出力を、同じ種類のアイテムが入っている近くのプレイヤー箱へまとめます。既定ではオフで、産物が建物の出力在庫から自動で移動しないようにします。"); dictionary.Add("config.item_processing_autofill.description", "手動粉ひき、水車、ドリウムなど加工ウィンドウを持つ建物が、選択したレシピに応じて近くのプレイヤー箱から自動補充できるようにします。"); dictionary.Add("config.player_chest_automation_radius.description", "工房が外の世界で近くのプレイヤー箱からアイテム素材を探す範囲。16 はおよそ 1 タイルです。"); dictionary.Add("config.trigger_quick_stack.description", "バニラのクイックスタックを使用したとき、近くへスマート入庫も実行します。"); dictionary.Add("config.trigger_sort.description", "バニラの整理を使用したとき、近くの整理も実行します。"); dictionary.Add("config.smart_deposit_hotkey.description", "押すと近くへスマート入庫を実行します。既定値: F7。"); dictionary.Add("config.sort_nearby_hotkey.description", "押すと近くの整理を実行します。既定値: F8。"); dictionary.Add("status.smart_deposit.success", "スマート入庫が完了しました!"); dictionary.Add("status.sort_nearby.success", "近くの整理が完了しました!"); dictionary.Add("storage_rules.title", "材料倉庫ルール"); dictionary.Add("storage_rules.loading", "倉庫ルールを読み込み中..."); dictionary.Add("storage_rules.footer", "ルールは自動入庫、手動投入、物流による材料倉庫への投入に影響します。取り出しや工房への供給は変わりません。許可されたアイテムがある場合はそれだけを受け取り、拒否されたアイテムは常に拒否されます。"); dictionary.Add("storage_rules.load_failed", "この材料倉庫のルールを読み込めませんでした。材料倉庫がまだアクセス範囲内にあるか確認してください。"); dictionary.Add("storage_rules.empty", "この材料倉庫には設定可能なアイテムがありません。先にアイテムを1つ入れてからルールを設定してください。"); dictionary.Add("storage_rules.mode.default", "既定"); dictionary.Add("storage_rules.mode.whitelist", "許可"); dictionary.Add("storage_rules.mode.blacklist", "拒否"); dictionary.Add("storage_rules.open_button", "ルール"); dictionary.Add("storage_rules.open_tooltip", "材料倉庫ルールを開く"); dictionary.Add("item_processing_autofill.title", "自動補充"); dictionary.Add("item_processing_autofill.enabled", "近くの箱から補充"); break; case "en": dictionary.Add("mod.name", "Smart Storage Logistics"); dictionary.Add("mod.description", "Adds smarter nearby chest, building-storage, and food-storage sorting, quick deposit, and automation access to player chests."); dictionary.Add("section.general", "General"); dictionary.Add("section.storage", "Storage"); dictionary.Add("section.logistics", "Logistics"); dictionary.Add("section.controls", "Controls"); dictionary.Add("entry.enabled", "Enable Mod"); dictionary.Add("entry.smart_deposit", "Nearby Smart Deposit"); dictionary.Add("entry.sort_nearby", "Nearby Sort"); dictionary.Add("entry.sort_player_inventory", "Sort Player Inventory"); dictionary.Add("entry.sort_nearby_chests", "Sort Nearby Storage"); dictionary.Add("entry.nearby_radius", "Nearby Radius"); dictionary.Add("entry.material_storage_reserved_slots", "Material Storage Reserve"); dictionary.Add("entry.auto_connect", "Auto Supply Workshops"); dictionary.Add("entry.well_water_logistics", "Well Water Logistics"); dictionary.Add("entry.automation_player_chests", "Player Chest Automation"); dictionary.Add("entry.pull_ingredients_from_player_chests", "Pull From Player Chests"); dictionary.Add("entry.deposit_outputs_to_player_chests", "Deposit To Player Chests"); dictionary.Add("entry.item_processing_autofill", "Item Processing Auto Refill"); dictionary.Add("entry.player_chest_automation_radius", "Workshop Chest Radius"); dictionary.Add("entry.trigger_quick_stack", "Quick Stack Trigger"); dictionary.Add("entry.trigger_sort", "Sort Trigger"); dictionary.Add("entry.smart_deposit_hotkey", "Smart Deposit Hotkey"); dictionary.Add("entry.sort_nearby_hotkey", "Nearby Sort Hotkey"); dictionary.Add("config.enabled.description", "Master switch for Smart Storage Logistics."); dictionary.Add("config.smart_deposit.description", "Moves matching player inventory items into nearby chests, building storage, or food storage that already contain the same item types."); dictionary.Add("config.sort_nearby.description", "Allows server-side sorting for the player inventory, nearby chests, building storage, and food storage."); dictionary.Add("config.sort_player_inventory.description", "Sort the player inventory when nearby sorting runs."); dictionary.Add("config.sort_nearby_chests.description", "Sort nearby chests, building storage, and food storage when nearby sorting runs."); dictionary.Add("config.nearby_radius.description", "Search radius for nearby sortable storage in world units. 16 is roughly one tile."); dictionary.Add("config.material_storage_reserved_slots.description", "Empty slots to keep free when nearby smart deposit moves items into material storage. Material storage only receives item types already present in that storage."); dictionary.Add("config.auto_connect.description", "Off by default. When enabled, runs once after buildings are loaded to create request logistics links from material storage buildings to same-town workshops."); dictionary.Add("config.well_water_logistics.description", "Allows wells to act as logistics output sources for water resources and lets concrete mixers request water through logistics when they run dry."); dictionary.Add("config.automation_player_chests.description", "Controls whether workshops and item-processing buildings can use nearby player-placed chests. Turning this off disables both automatic pulling and automatic depositing."); dictionary.Add("config.pull_ingredients_from_player_chests.description", "Allow workshops and item-processing buildings with a UI to pull recipe ingredients from nearby player chests."); dictionary.Add("config.deposit_outputs_to_player_chests.description", "Merge building output into nearby player chests that already contain matching items. Default off, so products are not automatically moved out of building output storage."); dictionary.Add("config.item_processing_autofill.description", "Allow item-processing buildings with a UI, such as manual mills, watermills, and dolia, to auto-refill from nearby player chests using the selected recipe."); dictionary.Add("config.player_chest_automation_radius.description", "Search radius for workshops pulling item ingredients from nearby player chests in the outside world. 16 is roughly one tile."); dictionary.Add("config.trigger_quick_stack.description", "Also run nearby smart deposit when the vanilla Quick Stack action is used."); dictionary.Add("config.trigger_sort.description", "Also run nearby sorting when the vanilla Sort action is used."); dictionary.Add("config.smart_deposit_hotkey.description", "Runs nearby smart deposit when pressed. Default: F7."); dictionary.Add("config.sort_nearby_hotkey.description", "Runs nearby sorting when pressed. Default: F8."); dictionary.Add("status.smart_deposit.success", "Smart deposit complete!"); dictionary.Add("status.sort_nearby.success", "Nearby sort complete!"); dictionary.Add("storage_rules.title", "Material Storage Rules"); dictionary.Add("storage_rules.loading", "Loading storage rules..."); dictionary.Add("storage_rules.footer", "Rules affect automatic deposit, manual deposits, and logistics depositing into material storage. Taking items out or supplying workshops is unchanged. If any item is set to Allow, only allowed items are accepted; blocked items are always rejected."); dictionary.Add("storage_rules.load_failed", "Could not load rules for this material storage. Make sure it is still within access range."); dictionary.Add("storage_rules.empty", "This material storage has no configurable items. Put an item in first, then set its rule."); dictionary.Add("storage_rules.mode.default", "Default"); dictionary.Add("storage_rules.mode.whitelist", "Allow"); dictionary.Add("storage_rules.mode.blacklist", "Block"); dictionary.Add("storage_rules.open_button", "Rules"); dictionary.Add("storage_rules.open_tooltip", "Open material storage rules"); dictionary.Add("item_processing_autofill.title", "Auto Refill"); dictionary.Add("item_processing_autofill.enabled", "Pull from nearby chests"); break; } return dictionary; } } internal sealed class LocalizationManager { private static LocalizationManager _instance; private readonly Dictionary<string, Dictionary<string, string>> _localizations = new Dictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase); private string _currentLocale = "en"; public static LocalizationManager Instance => _instance ?? (_instance = new LocalizationManager()); public string CurrentLocale => _currentLocale; public event Action LanguageChanged; private LocalizationManager() { Initialize(); } public void Initialize() { _localizations.Clear(); string[] supportedLanguages = LocalizationHelper.SupportedLanguages; foreach (string text in supportedLanguages) { Dictionary<string, string> defaultTranslations = LocalizationHelper.GetDefaultTranslations(text); if (defaultTranslations.Count > 0) { _localizations[text] = defaultTranslations; } } RefreshLanguage(forceNotify: false); } public void RefreshLanguage(bool forceNotify = true) { string text = DetectLocale(); if (!_localizations.ContainsKey(text)) { text = "en"; } bool num = !string.Equals(_currentLocale, text, StringComparison.OrdinalIgnoreCase); _currentLocale = text; if (num || forceNotify) { this.LanguageChanged?.Invoke(); } } public string GetText(string key, params object[] args) { RefreshLanguage(forceNotify: false); string text = TryGetText(_currentLocale, key) ?? TryGetText("en", key) ?? TryGetText("zh_CN", key) ?? key; if (args == null || args.Length == 0) { return text; } return string.Format(text, args); } private string TryGetText(string locale, string key) { if (!_localizations.TryGetValue(locale, out var value) || !value.TryGetValue(key, out var value2)) { return null; } return value2; } private static string DetectLocale() { if (!string.IsNullOrWhiteSpace(Globals.LanguageCode)) { return NormalizeLocale(Globals.LanguageCode); } string text = ReadSavedLocale(); if (!string.IsNullOrWhiteSpace(text)) { return NormalizeLocale(text); } return NormalizeLocale(CultureInfo.CurrentCulture.Name); } private static string ReadSavedLocale() { try { string path = Path.Combine(GamePathHelper.GetFolderPath((GameFolder)1, false), "localization.json"); if (!File.Exists(path)) { return null; } using FileStream utf8Json = File.OpenRead(path); using JsonDocument jsonDocument = JsonDocument.Parse((Stream)utf8Json, new JsonDocumentOptions { AllowTrailingCommas = true, CommentHandling = JsonCommentHandling.Skip }); JsonElement value; return (jsonDocument.RootElement.TryGetProperty("LanguageCode", out value) && value.ValueKind == JsonValueKind.String) ? value.GetString() : null; } catch { return null; } } private static string NormalizeLocale(string languageCode) { string text = (languageCode ?? string.Empty).Replace('-', '_'); if (text != null) { int length = text.Length; if (length != 2) { if (length != 5) { if (length == 7) { char c = text[6]; if (c != 's') { if (c == 't' && text == "zh_Hant") { goto IL_017a; } } else if (text == "zh_Hans") { goto IL_0174; } } } else { switch (text[3]) { case 'C': break; case 'T': goto IL_00cf; case 'H': goto IL_00e4; case 'M': goto IL_00f9; case 'J': goto IL_010b; case 'U': goto IL_011a; case 'G': goto IL_0129; default: goto IL_018c; } if (text == "zh_CN") { goto IL_0174; } } } else { char c = text[0]; if (c != 'e') { if (c == 'j' && text == "ja") { goto IL_0180; } } else if (text == "en") { goto IL_0186; } } } goto IL_018c; IL_0129: if (text == "en_GB") { goto IL_0186; } goto IL_018c; IL_0180: return "ja_JP"; IL_0186: return "en"; IL_010b: if (text == "ja_JP") { goto IL_0180; } goto IL_018c; IL_017a: return "zh_TW"; IL_0174: return "zh_CN"; IL_018c: return "en"; IL_00e4: if (text == "zh_HK") { goto IL_017a; } goto IL_018c; IL_00f9: if (text == "zh_M