Decompiled source of NewtCraftHub v1.4.3

plugins/NewtCraftHub.dll

Decompiled 10 hours ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using HarmonyLib;
using NewtCraftHub.Addons;
using NewtCraftHub.Core;
using NewtCraftHub.Integration;
using NewtCraftHub.Patches;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Rendering;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("NewtCraftHub")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.4.3.0")]
[assembly: AssemblyInformationalVersion("1.4.3+edd98dd116fb4b6f1d43b226fdeecf1118d0c4b5")]
[assembly: AssemblyProduct("NewtCraftHub")]
[assembly: AssemblyTitle("NewtCraftHub")]
[assembly: AssemblyVersion("1.4.3.0")]
namespace NewtCraftHub
{
	internal sealed class ConfigurationManagerAttributes
	{
		public bool? Browsable;
	}
	[BepInPlugin("newton.newtcraftboxes", "NewtCraftHub", "1.4.3")]
	[BepInProcess("valheim.exe")]
	[BepInProcess("valheim")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public enum WhenFull
		{
			NearestChest,
			DropAtFeet
		}

		public const string Guid = "newton.newtcraftboxes";

		public const string Name = "NewtCraftHub";

		public const string Version = "1.4.3";

		private const string RulesFileName = "newton.NewtCraftHub.yml";

		private static readonly string[] LegacyRulesFileNames = new string[2] { "newton.NewtCraftBoxes.yml", "newton.CraftyStorage.yml" };

		private const string LegacyConfigFileName = "newton.craftystorage.cfg";

		private Harmony _harmony;

		private FileSystemWatcher _watcher;

		private volatile bool _rulesDirty;

		private FileSystemWatcher _configWatcher;

		private volatile bool _configDirty;

		private float _configReloadAt = -1f;

		private ConfigEntry<bool> _enabled;

		private ConfigEntry<bool> _craft;

		private ConfigEntry<bool> _fillWithShift;

		private ConfigEntry<bool> _shared;

		private ConfigEntry<bool> _cookingTimers;

		private ConfigEntry<bool> _cookingBurnTime;

		private ConfigEntry<string> _cookReadyIn;

		private ConfigEntry<string> _cookDone;

		private ConfigEntry<string> _cookBurns;

		private ConfigEntry<string> _cookBurnt;

		private ConfigEntry<string> _cookPaused;

		private ConfigEntry<float> _range;

		private ConfigEntry<KeyboardShortcut> _toggleKey;

		private ConfigEntry<bool> _onlyPlayerBuilt;

		private ConfigEntry<bool> _includeVehicles;

		private ConfigEntry<bool> _respectWards;

		private ConfigEntry<bool> _skipInUse;

		private ConfigEntry<bool> _takeOwnership;

		private ConfigEntry<bool> _stations;

		private ConfigEntry<float> _refreshInterval;

		private ConfigEntry<bool> _showAvailable;

		private ConfigEntry<bool> _debug;

		private ConfigEntry<bool> _showContents;

		private ConfigEntry<bool> _hoverAscending;

		private ConfigEntry<int> _hoverMaxItems;

		private ConfigEntry<ContentsSort> _hoverSortBy;

		private ConfigEntry<string> _hoverEntry;

		private ConfigEntry<string> _hoverOverflow;

		private ConfigEntry<string> _hoverCapacity;

		private ConfigEntry<bool> _itemInfo;

		private ConfigEntry<bool> _itemType;

		private ConfigEntry<bool> _itemTier;

		private ConfigEntry<bool> _itemStack;

		private ConfigEntry<bool> _itemNearby;

		private ConfigEntry<bool> _itemPrefab;

		private ConfigEntry<bool> _worldClock;

		private ConfigEntry<bool> _clockShadow;

		private ConfigEntry<bool> _farmGrid;

		private ConfigEntry<bool> _shiftHarvest;

		private ConfigEntry<string> _clockFormat;

		private ConfigEntry<string> _clockTemplate;

		private ConfigEntry<string> _clockPosition;

		private ConfigEntry<string> _clockColor;

		private ConfigEntry<string> _clockPeriods;

		private ConfigEntry<string> _gridColor;

		private ConfigEntry<int> _clockFontSize;

		private ConfigEntry<int> _gridSections;

		private ConfigEntry<int> _harvestMax;

		private ConfigEntry<float> _gridExtra;

		private ConfigEntry<float> _harvestRadius;

		private ConfigEntry<float> _harvestStamina;

		private ConfigEntry<KeyboardShortcut> _clockKey;

		private ConfigEntry<bool> _objectHealth;

		private ConfigEntry<bool> _healthTrees;

		private ConfigEntry<bool> _healthRocks;

		private ConfigEntry<bool> _healthOther;

		private ConfigEntry<bool> _healthColours;

		private ConfigEntry<bool> _areaRepair;

		private ConfigEntry<bool> _repairPerPiece;

		private ConfigEntry<float> _repairRadius;

		private ConfigEntry<bool> _portalItems;

		private ConfigEntry<bool> _tpMarkers;

		private ConfigEntry<bool> _tpPlayers;

		private ConfigEntry<bool> _tpMultiplayer;

		private ConfigEntry<bool> _tpPortalRules;

		private ConfigEntry<float> _tpCooldown;

		private ConfigEntry<bool> _harvestBushes;

		private ConfigEntry<bool> _findItems;

		private ConfigEntry<bool> _creatureHealth;

		private ConfigEntry<bool> _healthPieces;

		private ConfigEntry<bool> _shipStatus;

		private ConfigEntry<bool> _shipWind;

		private ConfigEntry<bool> _shipPos;

		private ConfigEntry<string> _shipPosition;

		private ConfigEntry<int> _shipFont;

		private ConfigEntry<float> _findRange;

		private ConfigEntry<float> _findSeconds;

		private ConfigEntry<string> _findColor;

		private ConfigEntry<int> _tpMax;

		private ConfigEntry<bool> _extraSlots;

		private ConfigEntry<bool> _slotsAmmo;

		private ConfigEntry<bool> _slotsFood;

		private ConfigEntry<bool> _slotsMisc;

		private ConfigEntry<int> _slotsQuick;

		private ConfigEntry<int> _slotsUtility;

		private ConfigEntry<bool> _slotsHotbars;

		private ConfigEntry<bool> _eternalFires;

		private ConfigEntry<bool> _eternalTorches;

		private ConfigEntry<bool> _eternalCampfires;

		private ConfigEntry<bool> _skipIntro;

		private ConfigEntry<bool> _barsOnlyFocused;

		private ConfigEntry<bool> _recycle;

		private ConfigEntry<bool> _recycleButton;

		private ConfigEntry<float> _recyclePercent;

		private ConfigEntry<ModSettings.NoRecipe> _recycleNoRecipe;

		private ConfigEntry<WhenFull> _recycleFull;

		private ConfigEntry<KeyCode> _recycleKey;

		private ConfigEntry<string> _recycleLabel;

		private ConfigEntry<string> _recycleColor;

		private ConfigEntry<float> _barFade;

		private readonly ConfigEntry<KeyboardShortcut>[] _slotKeys = new ConfigEntry<KeyboardShortcut>[40];

		private const int CurrentConfigVersion = 3;

		private const string DefaultRules = "# NewtCraftHub rules: per-container restrictions for pulling items.\n# Changes are picked up live while the game is running.\n#\n# groups: reusable lists of ITEM PREFAB names (case sensitive).\n# <container prefab>:\n#   exclude:          items or groups that must NOT be pulled from this container type\n#   includeOverride:  items or groups that are always allowed (wins over exclude)\n#\n# Predefined groups: All, Food, Potion, Fish, Swords, Bows, Crossbows, Axes, Clubs, Knives,\n# Pickaxes, Polearms, Spears, Equipment, Trophy, Boss Trophy, Crops, Seeds, Ores, Metals, Woods.\n#\n# Example:\n# groups:\n#   Valuables:\n#     - Coins\n#     - Ruby\n# piece_chest_private:\n#   exclude:\n#     - All\n# piece_chest_wood:\n#   exclude:\n#     - Valuables\n#   includeOverride:\n#     - Coins\n";

		private string RulesPath => Path.Combine(Paths.ConfigPath, "newton.NewtCraftHub.yml");

		private void Awake()
		{
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Expected O, but got Unknown
			Log.Info = ((BaseUnityPlugin)this).Logger.LogInfo;
			Log.Warn = ((BaseUnityPlugin)this).Logger.LogWarning;
			Log.Error = ((BaseUnityPlugin)this).Logger.LogError;
			Log.Debug = delegate(string s)
			{
				if (Runtime.Settings.DebugLogging)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)("[debug] " + s));
				}
			};
			MigrateLegacyConfig();
			BindConfig();
			LoadRules();
			WatchRules();
			WatchConfig();
			SetUpCompatibility();
			((Component)this).gameObject.AddComponent<ClockHud>();
			((Component)this).gameObject.AddComponent<ShipHud>();
			((Component)this).gameObject.AddComponent<SlotBars>();
			Pictograms.PrepareInBackground(96);
			_harmony = new Harmony("newton.newtcraftboxes");
			int num = PatchInstaller.Apply(_harmony);
			((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("{0} {1} loaded ({2}/{3} patch groups active).", "NewtCraftHub", "1.4.3", num, PatchInstaller.PatchTypes.Length));
		}

		private void MigrateLegacyConfig()
		{
			try
			{
				string text = Path.Combine(Paths.ConfigPath, "newton.craftystorage.cfg");
				if (File.Exists(text) && !File.Exists(((BaseUnityPlugin)this).Config.ConfigFilePath))
				{
					File.Copy(text, ((BaseUnityPlugin)this).Config.ConfigFilePath);
					((BaseUnityPlugin)this).Config.Reload();
					((BaseUnityPlugin)this).Logger.LogInfo((object)"Imported settings from newton.craftystorage.cfg.");
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not import newton.craftystorage.cfg: " + ex.Message));
			}
		}

		private void BindConfig()
		{
			//IL_0352: Unknown result type (might be due to invalid IL or missing references)
			//IL_035c: Expected O, but got Unknown
			//IL_0372: Unknown result type (might be due to invalid IL or missing references)
			//IL_045a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0464: Expected O, but got Unknown
			//IL_04ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_04b8: Expected O, but got Unknown
			//IL_0708: Unknown result type (might be due to invalid IL or missing references)
			//IL_07b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_07ba: Expected O, but got Unknown
			//IL_0858: Unknown result type (might be due to invalid IL or missing references)
			//IL_0862: Expected O, but got Unknown
			//IL_0896: Unknown result type (might be due to invalid IL or missing references)
			//IL_08a0: Expected O, but got Unknown
			//IL_08f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0903: Expected O, but got Unknown
			//IL_0937: Unknown result type (might be due to invalid IL or missing references)
			//IL_0941: Expected O, but got Unknown
			//IL_096e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0978: Expected O, but got Unknown
			//IL_0a72: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a7c: Expected O, but got Unknown
			//IL_0ad1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0adb: Expected O, but got Unknown
			//IL_0b08: Unknown result type (might be due to invalid IL or missing references)
			//IL_0b12: Expected O, but got Unknown
			//IL_0ba9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0bb3: Expected O, but got Unknown
			//IL_0be7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0bf1: Expected O, but got Unknown
			//IL_0c65: Unknown result type (might be due to invalid IL or missing references)
			//IL_0c6f: Expected O, but got Unknown
			//IL_0cd9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0ce3: Expected O, but got Unknown
			//IL_0d0b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0d15: Expected O, but got Unknown
			//IL_0de9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0e4a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0ec1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0f19: Unknown result type (might be due to invalid IL or missing references)
			//IL_0f23: Expected O, but got Unknown
			//IL_1047: Unknown result type (might be due to invalid IL or missing references)
			//IL_1051: Expected O, but got Unknown
			_craft = ((BaseUnityPlugin)this).Config.Bind<bool>("0 - Features", "CraftFromContainers", true, "ON/OFF: craft, build and upgrade using materials from nearby containers.");
			_stations = ((BaseUnityPlugin)this).Config.Bind<bool>("0 - Features", "FeedStations", true, "ON/OFF: feed smelters, blast furnaces, kilns, windmills, spinning wheels, eitr refineries, fermenters and ovens from nearby containers (ore, fuel, mead bases, raw food) when your inventory has none.");
			_fillWithShift = ((BaseUnityPlugin)this).Config.Bind<bool>("0 - Features", "ShiftFillsStations", true, "ON/OFF: hold Shift while pressing use on a smelter, furnace, kiln, windmill, spinning wheel, eitr refinery or oven to add as much as it can take at once (from your inventory, and from containers when FeedStations is on).");
			_shared = ((BaseUnityPlugin)this).Config.Bind<bool>("0 - Features", "SharedContainers", true, "ON/OFF: several players can have the same chest open at once and move items in it (no more 'in use'). Every player using the chest needs the mod with this on; a player without it still gets 'in use'. Carts and ships are not shared.");
			_cookingTimers = ((BaseUnityPlugin)this).Config.Bind<bool>("0 - Features", "CookingTimers", true, "ON/OFF: countdown for each item on cooking stations, the iron cooking station and the stone oven (time until cooked, and until burnt). Options in '8 - Cooking Timers'.");
			_worldClock = ((BaseUnityPlugin)this).Config.Bind<bool>("0 - Features", "WorldClock", true, "ON/OFF: in-game time and day at the top of the screen (options in 'A - World Clock').");
			_farmGrid = ((BaseUnityPlugin)this).Config.Bind<bool>("0 - Features", "FarmGrid", true, "ON/OFF: crops planted with the cultivator snap to an even grid lined up with the crops around them; hold Shift (free placement) to plant anywhere (options in 'B - Farm Grid').");
			_shiftHarvest = ((BaseUnityPlugin)this).Config.Bind<bool>("0 - Features", "ShiftHarvest", true, "ON/OFF: Shift + use on a ripe crop harvests the crops around it (on a berry or fruit bush: every fruit bush around it), nearest first, as many as your stamina allows (options in 'C - Shift Harvest').");
			_objectHealth = ((BaseUnityPlugin)this).Config.Bind<bool>("0 - Features", "ObjectHealth", true, "ON/OFF: health of the tree, log, rock or ore deposit under your crosshair, e.g. 'Fir 47/60' (options in 'D - Object Health').");
			_areaRepair = ((BaseUnityPlugin)this).Config.Bind<bool>("0 - Features", "AreaRepair", true, "ON/OFF: repairing a piece with the hammer also repairs every damaged piece around it (options in 'E - Area Repair').");
			_portalItems = ((BaseUnityPlugin)this).Config.Bind<bool>("0 - Features", "PortalItems", true, "ON/OFF: ores, metals, dragon eggs and other restricted items go through portals (for you), without the 'cannot teleport' warning and icon.");
			_tpMarkers = ((BaseUnityPlugin)this).Config.Bind<bool>("0 - Features", "TeleportMarkers", true, "ON/OFF: personal teleport markers with /tp in chat: /tp create <name>, /tp <name>, /tp delete <name>, /tp list, /tp player <name> (options in 'F - Teleports').");
			_findItems = ((BaseUnityPlugin)this).Config.Bind<bool>("0 - Features", "FindItems", true, "ON/OFF: /find <item> in chat makes every container around you holding that item glow for a few seconds (options in 'G - Find').");
			_creatureHealth = ((BaseUnityPlugin)this).Config.Bind<bool>("0 - Features", "CreatureHealth", true, "ON/OFF: creature and boss health bars show their health as numbers next to the name, e.g. 'Greyling 12/20'.");
			_shipStatus = ((BaseUnityPlugin)this).Config.Bind<bool>("0 - Features", "ShipStatus", true, "ON/OFF: while you are on a boat: speed, sail, heading, wind and position above the sailing HUD (options in 'H - Ship Status').");
			_extraSlots = ((BaseUnityPlugin)this).Config.Bind<bool>("0 - Features", "ExtraSlots", true, "ON/OFF: a panel beside the inventory with equipment, extra utility, quick, ammo, food and misc slots (options in 'I - Extra Slots'). Stays off while shudnal's ExtraSlots is installed; items in the slots are kept either way.");
			_recycle = ((BaseUnityPlugin)this).Config.Bind<bool>("0 - Features", "Recycle", true, "ON/OFF: a Trash button beside the inventory recycles the item you drop on it (materials back) (options in 'L - Recycle'). Stays off while RecyclePlus is installed.");
			_eternalFires = ((BaseUnityPlugin)this).Config.Bind<bool>("0 - Features", "EternalFires", true, "ON/OFF: torches, braziers, campfires and hearths you built never run out of fuel once lit (options in 'J - Eternal Fires'). Stays off while TorchesEternal is installed.");
			_skipIntro = ((BaseUnityPlugin)this).Config.Bind<bool>("0 - Features", "SkipIntroVideo", true, "ON/OFF: the Deep North intro video doesn't play when the game opens (it can still be watched from the main menu's cinematics). Applies the next time the game starts.");
			_showContents = ((BaseUnityPlugin)this).Config.Bind<bool>("0 - Features", "ChestContentsOnHover", true, "ON/OFF: list what a chest, cart or ship storage holds when you look at it (options in '6 - Chest Contents').");
			_itemInfo = ((BaseUnityPlugin)this).Config.Bind<bool>("0 - Features", "ItemTooltipInfo", true, "ON/OFF: extra lines in item tooltips: type, tool tier, stack, amount in nearby containers (options in '7 - Item Tooltip').");
			_showAvailable = ((BaseUnityPlugin)this).Config.Bind<bool>("0 - Features", "AvailableAmountText", true, "ON/OFF: show 'available/required' on crafting and building requirements when containers contribute.");
			MigrateOrphan(_stations, "2 - Containers", "UseForStations");
			MigrateOrphan(_showAvailable, "4 - UI", "ShowAvailableAmount");
			_enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "Enabled", true, "Master switch for the whole mod (all features).");
			_range = ((BaseUnityPlugin)this).Config.Bind<float>("1 - General", "Range", 100f, new ConfigDescription("Maximum distance (meters) from the player to containers used for crafting/building.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 100f), Array.Empty<object>()));
			_toggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("1 - General", "ToggleKey", KeyboardShortcut.Empty, "Optional hotkey to turn pulling from containers on/off in game.");
			_onlyPlayerBuilt = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Containers", "OnlyPlayerBuilt", true, "Ignore containers that were not built by a player (dungeon/treasure chests).");
			_includeVehicles = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Containers", "IncludeShipsAndCarts", true, "Also use containers on ships and carts.");
			_respectWards = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Containers", "RespectWards", true, "Skip containers inside wards you don't have access to.");
			_skipInUse = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Containers", "SkipContainersInUse", true, "Skip containers currently opened by another player (recommended for multiplayer).");
			_takeOwnership = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Containers", "TakeNetworkOwnership", true, "Multiplayer: ask the player whose game currently controls a chest to hand it over (the same request the game makes when you open it) before using it. Required to use chests near other players; when off, only chests your game already controls are used.");
			_refreshInterval = ((BaseUnityPlugin)this).Config.Bind<float>("3 - Performance", "RefreshInterval", 0.5f, new ConfigDescription("Seconds between rescans of nearby containers while standing still. Item counts update instantly regardless.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 5f), Array.Empty<object>()));
			_debug = ((BaseUnityPlugin)this).Config.Bind<bool>("5 - Debug", "DebugLogging", false, "Verbose logging to BepInEx/LogOutput.log (and a 'Perf:' line every minute).");
			_hoverMaxItems = ((BaseUnityPlugin)this).Config.Bind<int>("6 - Chest Contents", "MaxEntries", 8, new ConfigDescription("How many items the list shows; the rest is summarised by OverflowFormat. -1 shows everything.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(-1, 100), Array.Empty<object>()));
			_hoverSortBy = ((BaseUnityPlugin)this).Config.Bind<ContentsSort>("6 - Chest Contents", "SortBy", ContentsSort.Amount, "Order of the list: Amount, Name, Weight (total) or Value (total).");
			_hoverAscending = ((BaseUnityPlugin)this).Config.Bind<bool>("6 - Chest Contents", "SortAscending", false, "Reverse the order (smallest first / Z to A).");
			_hoverEntry = ((BaseUnityPlugin)this).Config.Bind<string>("6 - Chest Contents", "EntryFormat", "<color=#FFFFAAFF>{0}</color> <color=#AAFFAAFF>{1}</color>", "One line per item. {0} = total amount, {1} = item name. Rich text colours allowed.");
			_hoverOverflow = ((BaseUnityPlugin)this).Config.Bind<string>("6 - Chest Contents", "OverflowFormat", "<color=#AAAAAAFF>+{0} more</color>", "Shown when there are more items than MaxEntries. {0} = how many are not listed. Empty = nothing.");
			_hoverCapacity = ((BaseUnityPlugin)this).Config.Bind<string>("6 - Chest Contents", "CapacityFormat", "<color=#AAAAAAFF>{0}/{1} slots</color>", "{0} = slots in use, {1} = total slots. Empty = nothing.");
			_itemType = ((BaseUnityPlugin)this).Config.Bind<bool>("7 - Item Tooltip", "ShowItemType", true, "Item type (Material, Tool, One Handed Weapon...).");
			_itemTier = ((BaseUnityPlugin)this).Config.Bind<bool>("7 - Item Tooltip", "ShowToolTier", true, "Tool tier of tools and weapons (what they can mine / chop).");
			_itemStack = ((BaseUnityPlugin)this).Config.Bind<bool>("7 - Item Tooltip", "ShowStackSize", true, "Stack size and maximum stack (e.g. 34/50).");
			_itemNearby = ((BaseUnityPlugin)this).Config.Bind<bool>("7 - Item Tooltip", "ShowNearbyCount", true, "How many of this item the nearby containers hold (within Range, respecting the rules file).");
			_itemPrefab = ((BaseUnityPlugin)this).Config.Bind<bool>("7 - Item Tooltip", "ShowPrefabName", false, "The item's internal (prefab) name, the one to write in the rules file.");
			_cookingBurnTime = ((BaseUnityPlugin)this).Config.Bind<bool>("8 - Cooking Timers", "ShowBurnTime", true, "When an item is cooked, also show how long until it burns.");
			_cookReadyIn = ((BaseUnityPlugin)this).Config.Bind<string>("8 - Cooking Timers", "ReadyInFormat", "<color=#FFFFAAFF>{0}</color> ready in <color=#AAFFAAFF>{1}</color>", "Item still cooking. {0} = item it becomes, {1} = time left (m:ss).");
			_cookDone = ((BaseUnityPlugin)this).Config.Bind<string>("8 - Cooking Timers", "DoneFormat", "<color=#FFFFAAFF>{0}</color> <color=#AAFFAAFF>ready</color>", "Item cooked (used when ShowBurnTime is off or the station never burns food). {0} = item.");
			_cookBurns = ((BaseUnityPlugin)this).Config.Bind<string>("8 - Cooking Timers", "BurnsFormat", "<color=#FFFFAAFF>{0}</color> <color=#AAFFAAFF>ready</color>, burns in <color=#FF9988FF>{1}</color>", "Item cooked and waiting. {0} = item, {1} = time until it burns (m:ss).");
			_cookBurnt = ((BaseUnityPlugin)this).Config.Bind<string>("8 - Cooking Timers", "BurntFormat", "<color=#FF9988FF>{0}</color> (burnt)", "Item burnt. {0} = what it turned into.");
			_cookPaused = ((BaseUnityPlugin)this).Config.Bind<string>("8 - Cooking Timers", "PausedText", "<color=#AAAAAAFF>(no heat)</color>", "Added to a line whose timer has stopped (fire out, no fuel). Empty = nothing.");
			_clockKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("A - World Clock", "ToggleKey", new KeyboardShortcut((KeyCode)278, Array.Empty<KeyCode>()), "Key that shows / hides the clock while playing.");
			_clockFormat = ((BaseUnityPlugin)this).Config.Bind<string>("A - World Clock", "TimeFormat", "HH:mm", "HH:mm = 24 h (14:05), hh:mm tt = 12 h (02:05 PM), or 'fuzzy' for the period name (Late morning...).");
			_clockTemplate = ((BaseUnityPlugin)this).Config.Bind<string>("A - World Clock", "Text", "<size={3}>Day {2}</size>\n<b>{0}</b>", "What is shown. {0} = time, {1} = period name, {2} = day number, {3} = a smaller font size (3/4 of FontSize, for <size={3}>). \\n = new line. Rich text (<b>, <color=#FFD700>...) allowed.");
			_clockPosition = ((BaseUnityPlugin)this).Config.Bind<string>("A - World Clock", "Position", "50%,2%", "Top-centre of the text: x,y in pixels or x%,y% of the screen.");
			_clockFontSize = ((BaseUnityPlugin)this).Config.Bind<int>("A - World Clock", "FontSize", 24, new ConfigDescription("Text size.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(8, 96), Array.Empty<object>()));
			_clockColor = ((BaseUnityPlugin)this).Config.Bind<string>("A - World Clock", "Color", "FFFFFFFF", "Text colour, RRGGBBAA.");
			_clockShadow = ((BaseUnityPlugin)this).Config.Bind<bool>("A - World Clock", "Shadow", true, "Dark shadow behind the text, readable on snow and sky.");
			_clockPeriods = ((BaseUnityPlugin)this).Config.Bind<string>("A - World Clock", "PeriodNames", string.Join(",", WorldClock.DefaultFuzzyNames), "Names for the periods of the day ({1} and TimeFormat = fuzzy), comma-separated, spread evenly from midnight.");
			_gridSections = ((BaseUnityPlugin)this).Config.Bind<int>("B - Farm Grid", "GridLines", 2, new ConfigDescription("How many grid cells to draw around the crop you are placing (0 = snap without drawing).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 10), Array.Empty<object>()));
			_gridExtra = ((BaseUnityPlugin)this).Config.Bind<float>("B - Farm Grid", "ExtraSpacing", 0.01f, new ConfigDescription("Metres added between crops, on top of the space they need to grow.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), Array.Empty<object>()));
			_gridColor = ((BaseUnityPlugin)this).Config.Bind<string>("B - Farm Grid", "LineColor", "FFFFFF40", "Grid line colour, RRGGBBAA (the last two digits are the transparency).");
			_harvestRadius = ((BaseUnityPlugin)this).Config.Bind<float>("C - Shift Harvest", "Radius", 15f, new ConfigDescription("Metres around the crop you press on.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 50f), Array.Empty<object>()));
			_harvestStamina = ((BaseUnityPlugin)this).Config.Bind<float>("C - Shift Harvest", "StaminaPerCrop", 4f, new ConfigDescription("Stamina used per crop; the harvest stops when you run out. 0 = free.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 50f), Array.Empty<object>()));
			_harvestMax = ((BaseUnityPlugin)this).Config.Bind<int>("C - Shift Harvest", "MaxCrops", 100, new ConfigDescription("Most crops one press can harvest.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 500), Array.Empty<object>()));
			_harvestBushes = ((BaseUnityPlugin)this).Config.Bind<bool>("C - Shift Harvest", "BerryBushes", true, "Shift + use on a berry or fruit bush (raspberries, blueberries, cloudberries...) picks every fruit bush around it, of any kind (mushrooms and other plants are left alone).");
			_healthTrees = ((BaseUnityPlugin)this).Config.Bind<bool>("D - Object Health", "Trees", true, "Trees, logs and stumps.");
			_healthRocks = ((BaseUnityPlugin)this).Config.Bind<bool>("D - Object Health", "RocksAndDeposits", true, "Big rocks and ore deposits (copper, tin, silver...): the total of every piece still standing.");
			_healthOther = ((BaseUnityPlugin)this).Config.Bind<bool>("D - Object Health", "OtherObjects", true, "Other things you break with tools: small rocks, bushes, stones...");
			_healthColours = ((BaseUnityPlugin)this).Config.Bind<bool>("D - Object Health", "Colours", true, "Current health in green, amber or red by how much is left.");
			_healthPieces = ((BaseUnityPlugin)this).Config.Bind<bool>("D - Object Health", "DamagedPieces", true, "Walls, floors, doors and other building pieces once they are damaged (intact ones show nothing).");
			_repairRadius = ((BaseUnityPlugin)this).Config.Bind<float>("E - Area Repair", "Radius", 15f, new ConfigDescription("Metres around the piece you repair.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 50f), Array.Empty<object>()));
			_repairPerPiece = ((BaseUnityPlugin)this).Config.Bind<bool>("E - Area Repair", "ChargePerPiece", false, "false = one repair's stamina and hammer durability per press; true = one per piece repaired.");
			_tpCooldown = ((BaseUnityPlugin)this).Config.Bind<float>("F - Teleports", "CooldownSeconds", 3f, new ConfigDescription("Seconds between two /tp teleports.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 600f), Array.Empty<object>()));
			_tpMax = ((BaseUnityPlugin)this).Config.Bind<int>("F - Teleports", "MaxMarkersPerWorld", 50, new ConfigDescription("Most markers per character in one world.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 500), Array.Empty<object>()));
			_tpPlayers = ((BaseUnityPlugin)this).Config.Bind<bool>("F - Teleports", "ToPlayers", true, "/tp player <name> teleports next to another player (only players who share their position on the map, like the minimap).");
			_tpMultiplayer = ((BaseUnityPlugin)this).Config.Bind<bool>("F - Teleports", "AllowInMultiplayer", true, "Allow /tp while other players are connected.");
			_tpPortalRules = ((BaseUnityPlugin)this).Config.Bind<bool>("F - Teleports", "FollowPortalRules", true, "When PortalItems is off, /tp refuses like a portal while you carry ores and other restricted items.");
			_findRange = ((BaseUnityPlugin)this).Config.Bind<float>("G - Find", "Range", 100f, new ConfigDescription("Metres around you that /find searches.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 300f), Array.Empty<object>()));
			_findSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("G - Find", "GlowSeconds", 5f, new ConfigDescription("How long the containers glow.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 30f), Array.Empty<object>()));
			_findColor = ((BaseUnityPlugin)this).Config.Bind<string>("G - Find", "GlowColor", "FF2A1AFF", "Glow colour, RRGGBBAA (bright red by default, apart from SargamAutoStore's green and blue).");
			_shipPosition = ((BaseUnityPlugin)this).Config.Bind<string>("H - Ship Status", "Position", "50%,74%", "Top-centre of the readout: x,y in pixels or x%,y% of the screen.");
			_shipFont = ((BaseUnityPlugin)this).Config.Bind<int>("H - Ship Status", "FontSize", 18, new ConfigDescription("Text size.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(8, 64), Array.Empty<object>()));
			_shipWind = ((BaseUnityPlugin)this).Config.Bind<bool>("H - Ship Status", "ShowWind", true, "Wind efficiency for the sail (the game's own formula) on the second line.");
			_shipPos = ((BaseUnityPlugin)this).Config.Bind<bool>("H - Ship Status", "ShowPosition", true, "Your X / Z coordinates on the second line.");
			_slotsQuick = ((BaseUnityPlugin)this).Config.Bind<int>("I - Extra Slots", "QuickSlots", 3, new ConfigDescription("Quick slots for potions and meads.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 3), Array.Empty<object>()));
			_slotsUtility = ((BaseUnityPlugin)this).Config.Bind<int>("I - Extra Slots", "ExtraUtilitySlots", 3, new ConfigDescription("Extra utility slots beside the equipment (belts, wishbone, charms...); their effects work like the one you wear.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 3), Array.Empty<object>()));
			_slotsAmmo = ((BaseUnityPlugin)this).Config.Bind<bool>("I - Extra Slots", "AmmoSlots", true, "3 slots for arrows and bolts.");
			_slotsFood = ((BaseUnityPlugin)this).Config.Bind<bool>("I - Extra Slots", "FoodSlots", true, "3 slots for food.");
			_slotsMisc = ((BaseUnityPlugin)this).Config.Bind<bool>("I - Extra Slots", "MiscSlots", true, "2 slots for keys.");
			_slotsHotbars = ((BaseUnityPlugin)this).Config.Bind<bool>("I - Extra Slots", "Hotbars", true, "A potion bar right of the Forsaken power with your quick slots, empty ones included (Alt + Z / X / C).");
			KeyCode[] array = new KeyCode[3];
			RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
			KeyCode[] array2 = (KeyCode[])(object)array;
			for (int i = 0; i < 3; i++)
			{
				_slotKeys[i] = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("I - Extra Slots", $"Quick{i + 1}Key", new KeyboardShortcut(array2[i], (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), $"Drinks the potion in quick slot {i + 1}.");
			}
			for (int j = 0; j < 3; j++)
			{
				_slotKeys[8 + j] = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("I - Extra Slots", $"Ammo{j + 1}Key", new KeyboardShortcut((KeyCode)(49 + j), (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), $"Equips the ammo in ammo slot {j + 1}.");
			}
			KeyCode[] array3 = new KeyCode[3];
			RuntimeHelpers.InitializeArray(array3, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
			KeyCode[] array4 = (KeyCode[])(object)array3;
			for (int k = 0; k < 3; k++)
			{
				_slotKeys[11 + k] = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("I - Extra Slots", $"Food{k + 1}Key", new KeyboardShortcut(array4[k], (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), $"Eats the food in food slot {k + 1}.");
			}
			_recyclePercent = ((BaseUnityPlugin)this).Config.Bind<float>("L - Recycle", "ReturnPercent", 100f, new ConfigDescription("How much of an item's materials come back, every upgrade level included (0-100).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			_recycleNoRecipe = ((BaseUnityPlugin)this).Config.Bind<ModSettings.NoRecipe>("L - Recycle", "ItemsWithoutRecipe", ModSettings.NoRecipe.Ask, "Items that give nothing back (no recipe, or fewer than one craft, e.g. 15 arrows): Ask = delete after a confirmation, Delete = delete at once, Never = keep them.");
			_recycleFull = ((BaseUnityPlugin)this).Config.Bind<WhenFull>("L - Recycle", "WhenInventoryFull", WhenFull.NearestChest, "Materials that don't fit in your inventory: into the nearest chest with room (else at your feet), or dropped at your feet.");
			_recycleKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("L - Recycle", "Hotkey", (KeyCode)127, "Recycles the item you are dragging (None = only the Trash button).");
			_recycleButton = ((BaseUnityPlugin)this).Config.Bind<bool>("L - Recycle", "ShowTrashButton", true, "The Trash button beside the inventory.");
			_recycleLabel = ((BaseUnityPlugin)this).Config.Bind<string>("L - Recycle", "TrashLabel", "Trash", "The button's label.");
			_recycleColor = ((BaseUnityPlugin)this).Config.Bind<string>("L - Recycle", "TrashLabelColor", "FFD800FF", "The label colour, RRGGBBAA.");
			_barsOnlyFocused = ((BaseUnityPlugin)this).Config.Bind<bool>("K - Creature Health", "OnlyWhileFocused", true, "A creature's health bar shows only while your crosshair is on it and hides as soon as you look away (bosses, players and your mount keep theirs). Off = the game's own delay.");
			_barFade = ((BaseUnityPlugin)this).Config.Bind<float>("K - Creature Health", "FadeOutSeconds", 0.5f, new ConfigDescription("How long a health bar takes to fade out after you look away (0 = at once).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 3f), Array.Empty<object>()));
			_eternalTorches = ((BaseUnityPlugin)this).Config.Bind<bool>("J - Eternal Fires", "Torches", true, "Standing torches, wall sconces and braziers never run out.");
			_eternalCampfires = ((BaseUnityPlugin)this).Config.Bind<bool>("J - Eternal Fires", "CampfiresAndHearths", true, "Campfires, hearths, bonfires, the hot tub and every other fire you build never run out.");
			UpgradeOldDefaults();
			ApplyConfig();
			((BaseUnityPlugin)this).Config.SettingChanged += delegate
			{
				ApplyConfig();
			};
		}

		private void UpgradeOldDefaults()
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Expected O, but got Unknown
			ConfigEntry<int> val = ((BaseUnityPlugin)this).Config.Bind<int>("9 - Internal", "ConfigVersion", 0, new ConfigDescription("Used by the mod to update old default values once. Don't change it.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Browsable = false
				}
			}));
			if (val.Value >= 3)
			{
				return;
			}
			if (val.Value < 1 && Math.Abs(_range.Value - 20f) < 0.01f)
			{
				_range.Value = 100f;
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Range was still the old default (20 m); updated to the new default, 100 m.");
			}
			if (val.Value < 2)
			{
				if (_clockTemplate.Value == "Day {2}  <b>{0}</b>")
				{
					_clockTemplate.Value = "<size={3}>Day {2}</size>\n<b>{0}</b>";
				}
				if (_clockFontSize.Value == 22)
				{
					_clockFontSize.Value = 24;
				}
			}
			if (val.Value < 3 && string.Equals(_findColor.Value, "FFC04DFF", StringComparison.OrdinalIgnoreCase))
			{
				_findColor.Value = "FF2A1AFF";
			}
			val.Value = 3;
			((BaseUnityPlugin)this).Config.Save();
		}

		private void MigrateOrphan(ConfigEntry<bool> entry, string oldSection, string oldKey)
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Expected O, but got Unknown
			try
			{
				Dictionary<ConfigDefinition, string> dictionary = typeof(ConfigFile).GetProperty("OrphanedEntries", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(((BaseUnityPlugin)this).Config, null) as Dictionary<ConfigDefinition, string>;
				ConfigDefinition key = new ConfigDefinition(oldSection, oldKey);
				if (dictionary != null && dictionary.TryGetValue(key, out var value))
				{
					if (bool.TryParse(value, out var result))
					{
						entry.Value = result;
					}
					dictionary.Remove(key);
					((BaseUnityPlugin)this).Config.Save();
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogDebug((object)("Config migration of " + oldSection + "/" + oldKey + ": " + ex.Message));
			}
		}

		private void ApplyConfig()
		{
			//IL_038f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0394: Unknown result type (might be due to invalid IL or missing references)
			//IL_043c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0435: Unknown result type (might be due to invalid IL or missing references)
			//IL_0441: Unknown result type (might be due to invalid IL or missing references)
			ModSettings settings = Runtime.Settings;
			settings.Enabled = _enabled.Value;
			settings.CraftFromContainers = _craft.Value;
			settings.Range = _range.Value;
			settings.OnlyPlayerBuilt = _onlyPlayerBuilt.Value;
			settings.IncludeVehicles = _includeVehicles.Value;
			settings.RespectWards = _respectWards.Value;
			settings.SkipContainersInUse = _skipInUse.Value;
			settings.TakeOwnership = _takeOwnership.Value;
			settings.UseForStations = _stations.Value;
			settings.FillStationsWithShift = _fillWithShift.Value;
			settings.SharedContainers = _shared.Value;
			settings.ShowCookingTimers = _cookingTimers.Value;
			settings.CookingShowBurnTime = _cookingBurnTime.Value;
			settings.CookingReadyInFormat = _cookReadyIn.Value;
			settings.CookingDoneFormat = _cookDone.Value;
			settings.CookingBurnsFormat = _cookBurns.Value;
			settings.CookingBurntFormat = _cookBurnt.Value;
			settings.CookingPausedText = _cookPaused.Value;
			settings.ShowWorldClock = _worldClock.Value;
			settings.ClockTimeFormat = _clockFormat.Value;
			settings.ClockTemplate = _clockTemplate.Value;
			settings.ClockPosition = _clockPosition.Value;
			settings.ClockFontSize = _clockFontSize.Value;
			settings.ClockColor = _clockColor.Value;
			settings.ClockShadow = _clockShadow.Value;
			settings.ClockPeriodNames = (string.IsNullOrWhiteSpace(_clockPeriods.Value) ? null : _clockPeriods.Value.Split(new char[1] { ',' }));
			settings.FarmGrid = _farmGrid.Value;
			settings.FarmGridSections = _gridSections.Value;
			settings.FarmGridExtraSpacing = _gridExtra.Value;
			settings.FarmGridColor = _gridColor.Value;
			settings.ShiftHarvest = _shiftHarvest.Value;
			settings.ShowObjectHealth = _objectHealth.Value;
			settings.ObjectHealthTrees = _healthTrees.Value;
			settings.ObjectHealthRocks = _healthRocks.Value;
			settings.ObjectHealthOther = _healthOther.Value;
			settings.ObjectHealthColours = _healthColours.Value;
			settings.ObjectHealthPieces = _healthPieces.Value;
			settings.CreatureHealth = _creatureHealth.Value;
			settings.ShowShipStatus = _shipStatus.Value;
			settings.ExtraSlots = _extraSlots.Value;
			settings.EternalFires = _eternalFires.Value;
			settings.SkipIntroVideo = _skipIntro.Value;
			settings.CreatureBarsOnlyFocused = _barsOnlyFocused.Value;
			settings.Recycle = _recycle.Value;
			settings.RecycleButton = _recycleButton.Value;
			settings.RecyclePercent = _recyclePercent.Value;
			settings.RecycleWithoutRecipe = _recycleNoRecipe.Value;
			settings.RecycleOverflowToChest = _recycleFull.Value == WhenFull.NearestChest;
			settings.RecycleLabel = _recycleLabel.Value;
			settings.RecycleLabelColor = _recycleColor.Value ?? "FFD800FF";
			Recycle.Key = _recycleKey.Value;
			settings.CreatureBarFadeSeconds = _barFade.Value;
			settings.EternalTorches = _eternalTorches.Value;
			settings.EternalCampfires = _eternalCampfires.Value;
			settings.Slots = new SlotLayout.Options(_slotsQuick.Value, _slotsUtility.Value, _slotsAmmo.Value, _slotsFood.Value, _slotsMisc.Value);
			settings.SlotHotbars = _slotsHotbars.Value;
			for (int i = 0; i < _slotKeys.Length; i++)
			{
				SlotBars.Keys[i] = _slotKeys[i]?.Value ?? KeyboardShortcut.Empty;
			}
			SlotBars.KeysChanged(((Component)this).GetComponent<SlotBars>());
			settings.ShipPosition = _shipPosition.Value;
			settings.ShipFontSize = _shipFont.Value;
			settings.ShipShowWind = _shipWind.Value;
			settings.ShipShowPosition = _shipPos.Value;
			settings.AreaRepair = _areaRepair.Value;
			settings.AreaRepairRadius = _repairRadius.Value;
			settings.AreaRepairChargePerPiece = _repairPerPiece.Value;
			settings.PortalItems = _portalItems.Value;
			settings.TeleportMarkers = _tpMarkers.Value;
			settings.TeleportCooldown = _tpCooldown.Value;
			settings.MaxMarkersPerWorld = _tpMax.Value;
			settings.TeleportToPlayers = _tpPlayers.Value;
			settings.TeleportInMultiplayer = _tpMultiplayer.Value;
			settings.MarkersFollowPortalRules = _tpPortalRules.Value;
			settings.HarvestRadius = _harvestRadius.Value;
			settings.HarvestStaminaPerCrop = _harvestStamina.Value;
			settings.HarvestMaxCrops = _harvestMax.Value;
			settings.HarvestBushes = _harvestBushes.Value;
			settings.FindItems = _findItems.Value;
			settings.FindRange = _findRange.Value;
			settings.FindGlowSeconds = _findSeconds.Value;
			settings.FindGlowColor = _findColor.Value;
			settings.RefreshInterval = _refreshInterval.Value;
			settings.ShowAvailableInUi = _showAvailable.Value;
			settings.ShowContentsOnHover = _showContents.Value;
			settings.HoverMaxItems = _hoverMaxItems.Value;
			settings.HoverSortBy = _hoverSortBy.Value;
			settings.HoverSortAscending = _hoverAscending.Value;
			settings.HoverEntryFormat = _hoverEntry.Value;
			settings.HoverOverflowFormat = _hoverOverflow.Value;
			settings.HoverCapacityFormat = _hoverCapacity.Value;
			settings.ShowItemInfo = _itemInfo.Value;
			settings.ShowItemType = _itemType.Value;
			settings.ShowToolTier = _itemTier.Value;
			settings.ShowStackSize = _itemStack.Value;
			settings.ShowNearbyCount = _itemNearby.Value;
			settings.ShowPrefabName = _itemPrefab.Value;
			settings.DebugLogging = _debug.Value;
			Perf.Enabled = settings.DebugLogging;
			settings.Revision++;
			Runtime.Index.Invalidate();
		}

		private void SetUpCompatibility()
		{
			Runtime.OtherModShowsChestContents = () => OtherPluginOptionIsOn("Azumatt.AzuHoverStats", "4 - Show Chest Contents", "Show Chest Contents");
			Runtime.OtherModShowsItemTypeAndTier = () => OtherPluginOptionIsOn("Azumatt.AzuHoverStats", "1 - General", "Show Tooltip Information");
			Runtime.OtherModShowsClock = Throttled(() => (OtherPluginOptionIsOn("aedenthorn.ClockMod", "General", "Enabled") && OtherPluginOptionIsOn("aedenthorn.ClockMod", "General", "ShowClock")) || OtherPluginOptionIsOn("Azumatt.AzuClock", "1 - General", "Show Clock"), "world clock", "another clock mod shows the time");
			Runtime.OtherModOwnsTeleportCommand = () => IsPluginLoaded("com.andersondavid.valheim.unrestrictedmineralportals");
			Runtime.OtherModRecycles = Throttled(() => IsPluginLoaded("TastyChickenLegs.RecyclePlus"), "recycling", "RecyclePlus has its own Trash button");
			Runtime.OtherModKeepsFiresLit = Throttled(() => IsPluginLoaded("Xenofell.TorchesEternal"), "eternal fires", "TorchesEternal keeps fires lit");
			bool? extraSlotsMod = null;
			Runtime.OtherModOwnsSlots = delegate
			{
				if (!extraSlotsMod.HasValue)
				{
					extraSlotsMod = IsPluginLoaded("shudnal.ExtraSlots");
					if (extraSlotsMod.Value)
					{
						((BaseUnityPlugin)this).Logger.LogInfo((object)"NewtCraftHub extra slots stay off: shudnal's ExtraSlots is installed (remove it to use ours; the items stay where they are).");
					}
				}
				return extraSlotsMod.Value;
			};
			Runtime.OtherModRepairsArea = Throttled(() => OtherPluginOptionIsOn("Azumatt.AzuAreaRepair", "1 - General", "Area Repair"), "area repair", "AzuAreaRepair repairs areas");
			Runtime.OtherModGridsCrops = Throttled(() => IsPluginLoaded("com.orianaventure.mod.VentureFarmGrid") || IsPluginLoaded("Moopfam.FarmGridRemake"), "farm grid", "another farm grid mod is installed");
		}

		private static bool IsPluginLoaded(string guid)
		{
			if (Chainloader.PluginInfos.TryGetValue(guid, out var value))
			{
				return (Object)(object)value.Instance != (Object)null;
			}
			return false;
		}

		private Func<bool> Throttled(Func<bool> check, string feature, string reason)
		{
			bool value = false;
			bool logged = false;
			float next = float.MinValue;
			return delegate
			{
				if (Time.time < next)
				{
					return value;
				}
				next = Time.time + 5f;
				try
				{
					value = check();
				}
				catch (Exception)
				{
					value = false;
				}
				if (value && !logged)
				{
					logged = true;
					((BaseUnityPlugin)this).Logger.LogInfo((object)("NewtCraftHub " + feature + " stays off: " + reason + " (turn one of them off to use the other)."));
				}
				return value;
			};
		}

		private static bool OtherPluginOptionIsOn(string guid, string section, string key)
		{
			try
			{
				if (!Chainloader.PluginInfos.TryGetValue(guid, out var value) || (Object)(object)value.Instance == (Object)null)
				{
					return false;
				}
				foreach (ConfigDefinition key2 in value.Instance.Config.Keys)
				{
					if (key2.Section == section && key2.Key == key)
					{
						return string.Equals(value.Instance.Config[key2].BoxedValue?.ToString(), "On", StringComparison.OrdinalIgnoreCase) || object.Equals(value.Instance.Config[key2].BoxedValue, true);
					}
				}
			}
			catch (Exception)
			{
			}
			return false;
		}

		private void LoadRules()
		{
			try
			{
				if (!File.Exists(RulesPath))
				{
					string text = null;
					string[] legacyRulesFileNames = LegacyRulesFileNames;
					foreach (string text2 in legacyRulesFileNames)
					{
						string text3 = Path.Combine(Paths.ConfigPath, text2);
						if (File.Exists(text3))
						{
							File.Copy(text3, RulesPath);
							text = text2;
							break;
						}
					}
					if (text != null)
					{
						((BaseUnityPlugin)this).Logger.LogInfo((object)("Imported your existing rules from " + text + "."));
					}
					else
					{
						File.WriteAllText(RulesPath, "# NewtCraftHub rules: per-container restrictions for pulling items.\n# Changes are picked up live while the game is running.\n#\n# groups: reusable lists of ITEM PREFAB names (case sensitive).\n# <container prefab>:\n#   exclude:          items or groups that must NOT be pulled from this container type\n#   includeOverride:  items or groups that are always allowed (wins over exclude)\n#\n# Predefined groups: All, Food, Potion, Fish, Swords, Bows, Crossbows, Axes, Clubs, Knives,\n# Pickaxes, Polearms, Spears, Equipment, Trophy, Boss Trophy, Crops, Seeds, Ores, Metals, Woods.\n#\n# Example:\n# groups:\n#   Valuables:\n#     - Coins\n#     - Ruby\n# piece_chest_private:\n#   exclude:\n#     - All\n# piece_chest_wood:\n#   exclude:\n#     - Valuables\n#   includeOverride:\n#     - Coins\n");
					}
				}
				PullRules pullRules = PullRules.FromYaml(File.ReadAllText(RulesPath));
				foreach (string warning in pullRules.Warnings)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)("newton.NewtCraftHub.yml: " + warning));
				}
				Runtime.SetRules(pullRules);
				((BaseUnityPlugin)this).Logger.LogInfo((object)$"Rules loaded: {pullRules.ContainerRuleCount} container rule(s), {pullRules.GroupCount} custom group(s).");
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Could not read newton.NewtCraftHub.yml, keeping previous rules: " + ex.Message));
			}
		}

		private void WatchRules()
		{
			try
			{
				_watcher = new FileSystemWatcher(Paths.ConfigPath, "newton.NewtCraftHub.yml")
				{
					NotifyFilter = (NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite)
				};
				FileSystemEventHandler value = delegate
				{
					_rulesDirty = true;
				};
				_watcher.Changed += value;
				_watcher.Created += value;
				_watcher.Renamed += delegate
				{
					_rulesDirty = true;
				};
				_watcher.EnableRaisingEvents = true;
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Live reload of newton.NewtCraftHub.yml unavailable: " + ex.Message));
			}
		}

		private void WatchConfig()
		{
			try
			{
				_configWatcher = new FileSystemWatcher(Path.GetDirectoryName(((BaseUnityPlugin)this).Config.ConfigFilePath), Path.GetFileName(((BaseUnityPlugin)this).Config.ConfigFilePath))
				{
					NotifyFilter = (NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite)
				};
				FileSystemEventHandler value = delegate
				{
					_configDirty = true;
				};
				_configWatcher.Changed += value;
				_configWatcher.Created += value;
				_configWatcher.Renamed += delegate
				{
					_configDirty = true;
				};
				_configWatcher.EnableRaisingEvents = true;
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Live reload of " + Path.GetFileName(((BaseUnityPlugin)this).Config.ConfigFilePath) + " unavailable: " + ex.Message));
			}
		}

		private void ReloadConfigIfChanged()
		{
			if (_configDirty)
			{
				_configDirty = false;
				_configReloadAt = Time.time + 0.5f;
			}
			if (_configReloadAt < 0f || Time.time < _configReloadAt)
			{
				return;
			}
			_configReloadAt = -1f;
			try
			{
				float range = Runtime.Settings.Range;
				((BaseUnityPlugin)this).Config.Reload();
				ApplyConfig();
				if (Math.Abs(range - Runtime.Settings.Range) > 0.001f)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)$"Settings reloaded: Range = {Runtime.Settings.Range} m.");
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not reload " + Path.GetFileName(((BaseUnityPlugin)this).Config.ConfigFilePath) + ": " + ex.Message));
			}
		}

		private void Update()
		{
			//IL_0034: 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_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			if (_rulesDirty)
			{
				_rulesDirty = false;
				LoadRules();
			}
			ReloadConfigIfChanged();
			Runtime.Tick();
			FarmGrid.Tick();
			FindCommand.Tick();
			KeyboardShortcut value = _clockKey.Value;
			if ((int)((KeyboardShortcut)(ref value)).MainKey != 0)
			{
				value = _clockKey.Value;
				if (((KeyboardShortcut)(ref value)).IsDown() && (Object)(object)Player.m_localPlayer != (Object)null)
				{
					ClockHud.ToggledOff = !ClockHud.ToggledOff;
				}
			}
			string text = Perf.EndFrame(Time.time);
			if (text != null)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)text);
			}
			value = _toggleKey.Value;
			if ((int)((KeyboardShortcut)(ref value)).MainKey == 0)
			{
				return;
			}
			value = _toggleKey.Value;
			if (((KeyboardShortcut)(ref value)).IsDown())
			{
				Runtime.ToggledOff = !Runtime.ToggledOff;
				Runtime.Index.Invalidate();
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer != (Object)null)
				{
					((Character)localPlayer).Message((MessageType)2, Runtime.ToggledOff ? "Crafting from containers: OFF" : "Crafting from containers: ON", 0, (Sprite)null, false);
				}
			}
		}

		private void OnDestroy()
		{
			_watcher?.Dispose();
			_configWatcher?.Dispose();
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			Scope.Reset();
		}
	}
	internal static class BuildInfo
	{
		public const string Version = "1.4.3";
	}
}
namespace NewtCraftHub.Patches
{
	public static class PatchInstaller
	{
		public static readonly Type[] PatchTypes = new Type[52]
		{
			typeof(ContainerAwakePatch),
			typeof(AugmentScopePatch),
			typeof(SuppressScopePatch),
			typeof(CountItemsPatch),
			typeof(HaveItemPatch),
			typeof(ConsumeScopePatch),
			typeof(RemoveItemPatch),
			typeof(FreshnessPatch),
			typeof(RequirementTextPatch),
			typeof(BuildRevalidatePatch),
			typeof(OwnershipResponsePatch),
			typeof(StationFeedPatch),
			typeof(StationHoverPatch),
			typeof(ContainerHoverPatch),
			typeof(ItemTooltipPatch),
			typeof(StationFillPatch),
			typeof(SharedOpenPatch),
			typeof(SharedViewPatch),
			typeof(SharedLoadPatch),
			typeof(SharedEditPatch),
			typeof(CookingTimerPatch),
			typeof(FarmGridPatch),
			typeof(ShiftHarvestPatch),
			typeof(ObjectHealthPatch),
			typeof(AreaRepairPatch),
			typeof(PortalItemsPatch),
			typeof(TeleportCommandsPatch),
			typeof(CreatureHealthPatch),
			typeof(EternalFiresPatch),
			typeof(RecycleShowPatch),
			typeof(RecycleDragPatch),
			typeof(SkipIntroPatch),
			typeof(SlotsRowsPatch),
			typeof(SlotsImportPatch),
			typeof(SlotsKeepOnLoadPatch),
			typeof(SlotsUtilityEffectsPatch),
			typeof(SlotsAddAtPatch),
			typeof(SlotsFreeSpacePatch),
			typeof(SlotsHaveEmptyPatch),
			typeof(SlotsUsedPercentagePatch),
			typeof(SlotsCanAddPatch),
			typeof(SlotsDropPatch),
			typeof(SlotsGuiPatch),
			typeof(SlotsGridPatch),
			typeof(SlotsEquipPatch),
			typeof(SlotsUnequipPatch),
			typeof(SlotsDeathPatch),
			typeof(SlotsGravePatch),
			typeof(SlotsGraveLoadPatch),
			typeof(SlotsTakeAllPatch),
			typeof(SlotsStackAllPatch),
			typeof(SlotHotkeyInputPatch)
		};

		public static int Apply(Harmony harmony)
		{
			int num = 0;
			Type[] patchTypes = PatchTypes;
			foreach (Type type in patchTypes)
			{
				try
				{
					if (harmony.CreateClassProcessor(type).Patch().Count == 0)
					{
						Log.Error("Patch " + type.Name + " hooked into nothing; that feature is disabled.");
					}
					else
					{
						num++;
					}
				}
				catch (Exception ex)
				{
					Log.Error("Patch " + type.Name + " could not be applied (" + ex.GetType().Name + ": " + ex.Message + "). That feature is disabled; the rest of the mod keeps working.");
				}
			}
			return num;
		}
	}
	[HarmonyPatch]
	internal static class ContainerAwakePatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			return Reflect.Methods(typeof(Container), "Awake");
		}

		private static void Postfix(Container __instance)
		{
			ContainerRegistry.Register(__instance);
			try
			{
				SharedContainers.Register(__instance);
			}
			catch (Exception ex)
			{
				Log.Warn("Shared chest setup failed for " + ((Object)__instance).name + ": " + ex.Message);
			}
		}
	}
	[HarmonyPatch]
	internal static class SharedOpenPatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			return Reflect.Methods(typeof(Container), "RPC_OpenResponse");
		}

		private static bool Prefix(Container __instance, bool granted)
		{
			if (!granted)
			{
				return SharedContainers.OnVanillaOpenRefused(__instance);
			}
			return true;
		}
	}
	[HarmonyPatch]
	internal static class SharedViewPatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			return Reflect.Methods(typeof(InventoryGui), "UpdateContainer");
		}

		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			MethodInfo objB = AccessTools.Method(typeof(Container), "IsOwner", (Type[])null, (Type[])null);
			MethodInfo operand = AccessTools.Method(typeof(SharedContainers), "CanView", (Type[])null, (Type[])null);
			List<CodeInstruction> list = instructions.ToList();
			int num = 0;
			foreach (CodeInstruction item in list)
			{
				if ((item.opcode == OpCodes.Callvirt || item.opcode == OpCodes.Call) && object.Equals(item.operand, objB))
				{
					item.opcode = OpCodes.Call;
					item.operand = operand;
					num++;
				}
			}
			if (num == 0)
			{
				throw new InvalidOperationException("Container.IsOwner check not found in InventoryGui.UpdateContainer");
			}
			return list;
		}
	}
	[HarmonyPatch]
	internal static class SharedLoadPatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			return Reflect.Methods(typeof(Container), "Load");
		}

		private static void Prefix(Container __instance)
		{
			SharedContainers.BeforeLoad(__instance);
		}
	}
	[HarmonyPatch]
	internal static class SharedEditPatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			foreach (MethodBase item in Reflect.Methods(typeof(InventoryGui), "OnSelectedItem"))
			{
				yield return item;
			}
			foreach (MethodBase item2 in Reflect.Methods(typeof(InventoryGui), "OnRightClickItem"))
			{
				yield return item2;
			}
			foreach (MethodBase item3 in Reflect.Methods(typeof(InventoryGui), "OnTakeAll"))
			{
				yield return item3;
			}
			foreach (MethodBase item4 in Reflect.Methods(typeof(InventoryGui), "OnStackAll"))
			{
				yield return item4;
			}
			foreach (MethodBase item5 in Reflect.Methods(typeof(InventoryGui), "OnDropOutside"))
			{
				yield return item5;
			}
		}

		private static bool Prefix(InventoryGui __instance, MethodBase __originalMethod, object[] __args)
		{
			//IL_005a: 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_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				SharedContainers.GuiAction action = __originalMethod.Name switch
				{
					"OnSelectedItem" => SharedContainers.GuiAction.Select, 
					"OnRightClickItem" => SharedContainers.GuiAction.RightClick, 
					"OnTakeAll" => SharedContainers.GuiAction.TakeAll, 
					"OnStackAll" => SharedContainers.GuiAction.StackAll, 
					_ => SharedContainers.GuiAction.DropOutside, 
				};
				InventoryGrid grid = null;
				ItemData item = null;
				Vector2i pos = default(Vector2i);
				Modifier mod = (Modifier)0;
				foreach (object obj in __args)
				{
					InventoryGrid val = (InventoryGrid)((obj is InventoryGrid) ? obj : null);
					if (val != null)
					{
						grid = val;
						continue;
					}
					ItemData val2 = (ItemData)((obj is ItemData) ? obj : null);
					if (val2 != null)
					{
						item = val2;
					}
					else if (obj is Vector2i val3)
					{
						pos = val3;
					}
					else if (obj is Modifier val4)
					{
						mod = val4;
					}
				}
				return SharedContainers.BeforeGuiAction(__instance, action, grid, item, pos, mod);
			}
			catch (Exception ex)
			{
				Log.Warn("Shared chest click failed: " + ex.Message);
				return !SharedContainers.ShowsChestOwnedByOther(__instance);
			}
		}
	}
	[HarmonyPatch]
	internal static class AugmentScopePatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			foreach (MethodBase item in Reflect.Methods(typeof(Player), "HaveRequirements"))
			{
				yield return item;
			}
			foreach (MethodBase item2 in Reflect.Methods(typeof(Player), "HaveRequirementItems"))
			{
				yield return item2;
			}
			foreach (MethodBase item3 in Reflect.Methods(typeof(InventoryGui), "SetupRequirement"))
			{
				yield return item3;
			}
		}

		private static void Prefix(object __0, out bool __state)
		{
			Recipe val = (Recipe)((__0 is Recipe) ? __0 : null);
			__state = val == null || !val.m_requireOnlyOneIngredient;
			if (__state)
			{
				Scope.Augment++;
			}
		}

		private static Exception Finalizer(Exception __exception, bool __state)
		{
			if (__state)
			{
				Scope.Exit(ref Scope.Augment);
			}
			return __exception;
		}
	}
	[HarmonyPatch]
	internal static class SuppressScopePatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			return Reflect.Methods(typeof(Player), "UpdateKnownRecipesList");
		}

		private static void Prefix()
		{
			Scope.Suppress++;
		}

		private static Exception Finalizer(Exception __exception)
		{
			Scope.Exit(ref Scope.Suppress);
			return __exception;
		}
	}
	[HarmonyPatch]
	internal static class CountItemsPatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			return Reflect.Methods(typeof(Inventory), "CountItems", (MethodInfo m) => Reflect.FirstParamIs<string>(m) && m.ReturnType == typeof(int));
		}

		private static void Postfix(Inventory __instance, string name, int quality, ref int __result)
		{
			if (Scope.Augmenting && !Scope.Consuming && name != null && Runtime.CraftActive && Runtime.IsLocalPlayerInventory(__instance))
			{
				__result += Runtime.CountNearby(name, quality);
			}
		}
	}
	[HarmonyPatch]
	internal static class HaveItemPatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			return Reflect.Methods(typeof(Inventory), "HaveItem", (MethodInfo m) => Reflect.FirstParamIs<string>(m) && m.ReturnType == typeof(bool));
		}

		private static void Postfix(Inventory __instance, string name, ref bool __result)
		{
			if (!__result && Scope.Augmenting && !Scope.Consuming && name != null && Runtime.CraftActive && Runtime.IsLocalPlayerInventory(__instance) && Runtime.CountNearby(name, -1) > 0)
			{
				__result = true;
			}
		}
	}
	[HarmonyPatch]
	internal static class ConsumeScopePatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			return Reflect.Methods(typeof(Player), "ConsumeResources");
		}

		private static void Prefix(Player __instance)
		{
			if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
			{
				Scope.Consume++;
				Runtime.Ledger.Begin();
				Runtime.Index.Invalidate();
			}
		}

		private static Exception Finalizer(Player __instance, Exception __exception)
		{
			if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer)
			{
				Scope.Exit(ref Scope.Consume);
				foreach (ConsumptionLedger.Entry item in Runtime.Ledger.Unsatisfied())
				{
					Log.Warn($"Could only provide {item.Provided}/{item.Shortfall} of {item.Item} from nearby containers (another player may have taken them).");
				}
			}
			return __exception;
		}
	}
	[HarmonyPatch]
	internal static class RemoveItemPatch
	{
		internal struct State
		{
			public int Before;

			public int Amount;
		}

		private static IEnumerable<MethodBase> TargetMethods()
		{
			return Reflect.Methods(typeof(Inventory), "RemoveItem", delegate(MethodInfo m)
			{
				ParameterInfo[] parameters = m.GetParameters();
				return parameters.Length >= 2 && parameters[0].ParameterType == typeof(string) && parameters[1].ParameterType == typeof(int);
			});
		}

		private static void Prefix(Inventory __instance, string name, int amount, int itemQuality, out State __state)
		{
			__state = new State
			{
				Before = -1,
				Amount = amount
			};
			if (Scope.Consuming && name != null && Runtime.CraftActive && Runtime.IsLocalPlayerInventory(__instance))
			{
				__state.Before = Runtime.CountInInventory(__instance, name, itemQuality);
			}
		}

		private static void Postfix(Inventory __instance, string name, int itemQuality, State __state)
		{
			if (__state.Before >= 0)
			{
				int amount = __state.Amount;
				int num = __state.Before - Runtime.CountInInventory(__instance, name, itemQuality);
				int num2 = amount - num;
				if (num2 > 0)
				{
					int provided = Runtime.TakeNearby(name, num2, itemQuality);
					Runtime.Ledger.Record(name, num2, provided);
				}
			}
		}
	}
	[HarmonyPatch]
	internal static class FreshnessPatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			return Reflect.Methods(typeof(InventoryGui), "DoCrafting");
		}

		private static void Prefix()
		{
			Runtime.Index.Invalidate();
		}
	}
	[HarmonyPatch]
	internal static class BuildRevalidatePatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			return Reflect.Methods(typeof(Player), "TryPlacePiece");
		}

		private static bool Prefix(Player __instance, Piece piece, ref bool __result)
		{
			if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer || !Runtime.CraftActive || (Object)(object)piece == (Object)null || __instance.NoCostCheat())
			{
				return true;
			}
			Runtime.Index.Invalidate();
			if (__instance.HaveRequirements(piece, (RequirementMode)0))
			{
				return true;
			}
			__result = false;
			return false;
		}
	}
	[HarmonyPatch]
	internal static class OwnershipResponsePatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			return Reflect.Methods(typeof(Container), "RPC_StackResponse");
		}

		private static bool Prefix(Container __instance, object[] __args)
		{
			if (!OwnershipRequests.Consume(__instance))
			{
				return true;
			}
			bool flag = default(bool);
			int num;
			if (__args.Length > 1)
			{
				object obj = __args[1];
				if (obj is bool)
				{
					flag = (bool)obj;
					num = 1;
				}
				else
				{
					num = 0;
				}
			}
			else
			{
				num = 0;
			}
			bool flag2 = (byte)((uint)num & (flag ? 1u : 0u)) != 0;
			if (!flag2)
			{
				DeferredTakes.OnDenied(__instance);
			}
			Log.Debug(flag2 ? ("Owner handed over " + ((Object)__instance).name) : ("Owner kept " + ((Object)__instance).name + " (open by someone)"));
			return false;
		}
	}
	[HarmonyPatch]
	internal static class CookingTimerPatch
	{
		private static bool s_warned;

		private static IEnumerable<MethodBase> TargetMethods()
		{
			return Reflect.Methods(typeof(CookingStation), "HoverText", (MethodInfo m) => m.ReturnType == typeof(string) && m.GetParameters().Length == 0);
		}

		private static void Postfix(CookingStation __instance, ref string __result)
		{
			try
			{
				using (Perf.Measure(Perf.Feature.CookingTimers))
				{
					__result = CookingTimers.Append(__instance, __result);
				}
			}
			catch (Exception ex)
			{
				if (!s_warned)
				{
					s_warned = true;
					Log.Warn("Cooking timers: " + ex.Message + " (check the formats in '8 - Cooking Timers')");
				}
			}
		}
	}
	[HarmonyPatch]
	internal static class StationFeedPatch
	{
		internal sealed class Use
		{
			public StationSupply.Loan Loan;

			public bool Counted;

			public float UsedBefore;
		}

		private static IEnumerable<MethodBase> TargetMethods()
		{
			foreach (MethodBase item in Reflect.Methods(typeof(Smelter), "OnAddOre"))
			{
				yield return item;
			}
			foreach (MethodBase item2 in Reflect.Methods(typeof(Smelter), "OnAddFuel"))
			{
				yield return item2;
			}
			foreach (MethodBase item3 in Reflect.Methods(typeof(CookingStation), "OnInteract"))
			{
				yield return item3;
			}
			foreach (MethodBase item4 in Reflect.Methods(typeof(CookingStation), "OnAddFuelSwitch"))
			{
				yield return item4;
			}
			foreach (MethodBase item5 in Reflect.Methods(typeof(Fermenter), "Interact"))
			{
				yield return item5;
			}
		}

		private static bool Prefix(object __instance, object[] __args, MethodBase __originalMethod, ref bool __result, out Use __state)
		{
			__state = null;
			try
			{
				if (StationCapacity.FullWithPending(__instance, __originalMethod.Name, out var counted, out var usedBefore))
				{
					bool flag = default(bool);
					int num;
					if (__args.Length > 1)
					{
						object obj = __args[1];
						if (obj is bool)
						{
							flag = (bool)obj;
							num = 1;
						}
						else
						{
							num = 0;
						}
					}
					else
					{
						num = 0;
					}
					bool flag2 = (byte)((uint)num & (flag ? 1u : 0u)) != 0;
					foreach (object obj2 in __args)
					{
						Humanoid val = (Humanoid)((obj2 is Humanoid) ? obj2 : null);
						if (val != null && !flag2)
						{
							((Character)val).Message((MessageType)2, "$msg_itsfull", 0, (Sprite)null, false);
							break;
						}
					}
					__result = false;
					return false;
				}
				__state = new Use
				{
					Counted = counted,
					UsedBefore = usedBefore,
					Loan = StationSupply.BeforeUse(__instance, __originalMethod.Name, __args)
				};
			}
			catch (Exception ex)
			{
				Log.Warn("Station supply failed: " + ex.Message);
			}
			return true;
		}

		private static void Postfix(object __instance, MethodBase __originalMethod, bool __result, Use __state)
		{
			if (__state == null || !__state.Counted || !__result)
			{
				return;
			}
			try
			{
				StationCapacity.Sent(__instance, __originalMethod.Name, __state.UsedBefore);
			}
			catch (Exception ex)
			{
				Log.Debug("Station limit tracking: " + ex.Message);
			}
		}

		private static Exception Finalizer(Exception __exception, Use __state)
		{
			StationSupply.AfterUse(__state?.Loan);
			return __exception;
		}
	}
	[HarmonyPatch]
	internal static class ContainerHoverPatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			return Reflect.Methods(typeof(Container), "GetHoverText");
		}

		private static void Postfix(Container __instance, ref string __result)
		{
			try
			{
				string text = ContainerContents.Describe(__instance);
				if (text != null)
				{
					__result += text;
				}
			}
			catch (Exception ex)
			{
				Log.Debug("ContainerHoverPatch: " + ex.Message);
			}
		}
	}
	[HarmonyPatch]
	internal static class ItemTooltipPatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			return Reflect.Methods(typeof(ItemData), "GetTooltip", (MethodInfo m) => m.IsStatic && Reflect.FirstParamIs<ItemData>(m));
		}

		private static void Postfix(ItemData item, bool crafting, bool appending, ref string __result)
		{
			try
			{
				if (!appending)
				{
					string text;
					using (Perf.Measure(Perf.Feature.ItemTooltip))
					{
						text = ItemTooltip.Describe(item, crafting);
					}
					if (text != null)
					{
						__result += text;
					}
				}
			}
			catch (Exception ex)
			{
				Log.Debug("ItemTooltipPatch: " + ex.Message);
			}
		}
	}
	[HarmonyPatch]
	internal static class StationFillPatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			return Reflect.Methods(typeof(Switch), "Interact");
		}

		private static bool Prefix(Switch __instance, Humanoid character, bool hold, bool alt, ref bool __result)
		{
			if (!alt || hold)
			{
				return true;
			}
			try
			{
				if (!StationSupply.TryFill(__instance, character, out var added))
				{
					return true;
				}
				__result = added;
				return false;
			}
			catch (Exception ex)
			{
				Log.Warn("Station fill failed: " + ex.Message);
				return true;
			}
		}
	}
	[HarmonyPatch]
	internal static class StationHoverPatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			foreach (MethodBase item in Reflect.Methods(typeof(Smelter), "OnHoverAddOre"))
			{
				yield return item;
			}
			foreach (MethodBase item2 in Reflect.Methods(typeof(Smelter), "OnHoverAddFuel"))
			{
				yield return item2;
			}
			foreach (MethodBase item3 in Reflect.Methods(typeof(CookingStation), "GetHoverText"))
			{
				yield return item3;
			}
			foreach (MethodBase item4 in Reflect.Methods(typeof(CookingStation), "OnHoverFuelSwitch"))
			{
				yield return item4;
			}
			foreach (MethodBase item5 in Reflect.Methods(typeof(Fermenter), "GetHoverText"))
			{
				yield return item5;
			}
		}

		private static void Postfix(object __instance, MethodBase __originalMethod)
		{
			try
			{
				StationSupply.Warm(__instance, __originalMethod.Name);
			}
			catch (Exception ex)
			{
				Log.Debug("StationHoverPatch: " + ex.Message);
			}
		}
	}
	[HarmonyPatch]
	internal static class RequirementTextPatch
	{
		private static readonly Dictionary<Type, PropertyInfo> s_textProps = new Dictionary<Type, PropertyInfo>();

		private static IEnumerable<MethodBase> TargetMethods()
		{
			return Reflect.Methods(typeof(InventoryGui), "SetupRequirement");
		}

		private static void Postfix(Transform elementRoot, Requirement req, bool craft)
		{
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!Runtime.Settings.ShowAvailableInUi || !Runtime.CraftActive)
				{
					return;
				}
				string text = req?.m_resItem?.m_itemData?.m_shared?.m_name;
				if ((Object)(object)elementRoot == (Object)null || text == null)
				{
					return;
				}
				int num = Runtime.CountNearby(text, -1);
				if (num <= 0)
				{
					return;
				}
				Transform val = elementRoot.Find("res_amount");
				if ((Object)(object)val == (Object)null)
				{
					return;
				}
				PropertyInfo prop;
				Component val2 = FindTextComponent(((Component)val).gameObject, out prop);
				if ((Object)(object)val2 == (Object)null)
				{
					return;
				}
				string text2 = prop.GetValue(val2, null) as string;
				if (!string.IsNullOrEmpty(text2) && text2.IndexOf('/') < 0)
				{
					int num2 = Available(text, req, craft, num);
					prop.SetValue(val2, $"{num2}/{text2}", null);
					if (int.TryParse(text2, out var result) && num2 < result)
					{
						((object)val2).GetType().GetProperty("color", typeof(Color))?.SetValue(val2, Color.red, null);
					}
				}
			}
			catch (Exception ex)
			{
				Log.Debug("RequirementTextPatch: " + ex.Message);
			}
		}

		private static int Available(string itemName, Requirement req, bool craft, int fromChestsAnyQuality)
		{
			Inventory inventory = ((Humanoid)Player.m_localPlayer).GetInventory();
			int maxQuality = req.m_resItem.m_itemData.m_shared.m_maxQuality;
			if (!craft || maxQuality <= 1)
			{
				return Runtime.CountInInventory(inventory, itemName, -1) + fromChestsAnyQuality;
			}
			int num = 0;
			for (int i = 1; i <= maxQuality; i++)
			{
				num = Math.Max(num, Runtime.CountInInventory(inventory, itemName, i) + Runtime.CountNearby(itemName, i));
			}
			return num;
		}

		private static Component FindTextComponent(GameObject go, out PropertyInfo prop)
		{
			Component[] components = go.GetComponents<Component>();
			foreach (Component val in components)
			{
				if (!((Object)(object)val == (Object)null))
				{
					Type type = ((object)val).GetType();
					if (!s_textProps.TryGetValue(type, out prop))
					{
						prop = ((type.Name.IndexOf("Text", StringComparison.Ordinal) >= 0) ? type.GetProperty("text", typeof(string)) : null);
						s_textProps[type] = prop;
					}
					if (prop != null && prop.CanWrite)
					{
						return val;
					}
				}
			}
			prop = null;
			return null;
		}
	}
}
namespace NewtCraftHub.Integration
{
	public static class ContainerContents
	{
		private struct Cached
		{
			public uint Revision;

			public int SettingsRevision;

			public ModSettings Settings;

			public string Text;
		}

		private sealed class Entry
		{
			public string Name;

			public string Label;

			public int Amount;

			public float Weight;

			public int Value;
		}

		private static readonly Dictionary<Container, Cached> s_cache = new Dictionary<Container, Cached>(ReferenceComparer<Container>.Instance);

		private static readonly Dictionary<string, Entry> s_byName = new Dictionary<string, Entry>(StringComparer.Ordinal);

		private static readonly List<Entry> s_sorted = new List<Entry>();

		private static readonly StringBuilder s_sb = new StringBuilder(256);

		private static bool s_warnedFormat;

		private static bool s_warnedOtherMod;

		private static readonly Func<Container, long, bool> s_checkAccess = Reflect.InstanceFunc<Container, long, bool>("CheckAccess");

		public static string Describe(Container container)
		{
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			ModSettings settings = Runtime.Settings;
			if (!settings.Enabled || !settings.ShowContentsOnHover || (Object)(object)container == (Object)null)
			{
				return null;
			}
			if (Runtime.OtherModShowsChestContents())
			{
				if (!s_warnedOtherMod)
				{
					s_warnedOtherMod = true;
					Log.Warn("AzuHoverStats is also listing chest contents; NewtCraftHub' list is skipped. Turn off 'Show Chest Contents' in AzuHoverStats (or remove it) to use ours.");
				}
				return null;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return null;
			}
			if (s_checkAccess == null || !s_checkAccess(container, localPlayer.GetPlayerID()))
			{
				return null;
			}
			if (container.m_checkGuardStone && !PrivateArea.CheckAccess(((Component)container).transform.position, 0f, false, false))
			{
				return null;
			}
			if (!ContainerRegistry.TryGet(container, out var src) || !src.IsAlive)
			{
				return null;
			}
			Inventory inventory = container.GetInventory();
			uint revision = src.Revision;
			if (s_cache.TryGetValue(container, out var value) && value.Revision == revision && value.Settings == settings && value.SettingsRevision == settings.Revision)
			{
				return value.Text;
			}
			if (s_cache.Count > 512)
			{
				s_cache.Clear();
			}
			Perf.Enter();
			string text;
			try
			{
				text = Build(inventory, settings);
			}
			finally
			{
				Perf.Exit();
			}
			s_cache[container] = new Cached
			{
				Revision = revision,
				Settings = settings,
				SettingsRevision = settings.Revision,
				Text = text
			};
			return text;
		}

		private static string Build(Inventory inv, ModSettings s)
		{
			s_byName.Clear();
			List<ItemData> allItems = inv.GetAllItems();
			for (int i = 0; i < allItems.Count; i++)
			{
				ItemData val = allItems[i];
				string text = val?.m_shared?.m_name;
				if (!string.IsNullOrEmpty(text))
				{
					if (!s_byName.TryGetValue(text, out var value))
					{
						Dictionary<string, Entry> dictionary = s_byName;
						Entry obj = new Entry
						{
							Name = text,
							Label = Localize(text)
						};
						value = obj;
						dictionary[text] = obj;
					}
					value.Amount += val.m_stack;
					value.Weight += val.m_shared.m_weight * (float)val.m_stack;
					value.Value += val.m_shared.m_value * val.m_stack;
				}
			}
			if (s_byName.Count == 0)
			{
				return null;
			}
			s_sorted.Clear();
			s_sorted.AddRange(s_byName.Values);
			s_sorted.Sort(delegate(Entry a, Entry b)
			{
				int num3 = s.HoverSortBy switch
				{
					ContentsSort.Name => string.Compare(a.Label, b.Label, StringComparison.CurrentCultureIgnoreCase), 
					ContentsSort.Weight => a.Weight.CompareTo(b.Weight), 
					ContentsSort.Value => a.Value.CompareTo(b.Value), 
					_ => a.Amount.CompareTo(b.Amount), 
				};
				if (s.HoverSortBy != ContentsSort.Name)
				{
					num3 = -num3;
				}
				if (s.HoverSortAscending)
				{
					num3 = -num3;
				}
				return (num3 == 0) ? string.Compare(a.Label, b.Label, StringComparison.CurrentCultureIgnoreCase) : num3;
			});
			s_sb.Clear();
			int num = ((s.HoverMaxItems < 0) ? s_sorted.Count : Math.Min(Math.Max(1, s.HoverMaxItems), s_sorted.Count));
			for (int num2 = 0; num2 < num; num2++)
			{
				s_sb.Append('\n').Append(Format(s.HoverEntryFormat, "<color=#FFFFAAFF>{0}</color> <color=#AAFFAAFF>{1}</color>", s_sorted[num2].Amount, s_sorted[num2].Label));
			}
			if (s_sorted.Count > num && !string.IsNullOrEmpty(s.HoverOverflowFormat))
			{
				s_sb.Append('\n').Append(Format(s.HoverOverflowFormat, "<color=#AAAAAAFF>+{0} more</color>", s_sorted.Count - num, null));
			}
			if (!string.IsNullOrEmpty(s.HoverCapacityFormat))
			{
				s_sb.Append('\n').Append(Format(s.HoverCapacityFormat, "<color=#AAAAAAFF>{0}/{1} slots</color>", inv.NrOfItems(), inv.GetWidth() * inv.GetHeight()));
			}
			return s_sb.ToString();
		}

		private static string Format(string format, string fallback, object a, object b)
		{
			try
			{
				return string.Format(format, a, b);
			}
			catch (FormatException)
			{
				if (!s_warnedFormat)
				{
					s_warnedFormat = true;
					Log.Warn("Invalid hover format '" + format + "': using the default.");
				}
				return string.Format(fallback, a, b);
			}
		}

		private static string Localize(string token)
		{
			try
			{
				return (Localization.instance != null) ? Localization.instance.Localize(token) : token;
			}
			catch
			{
				return token;
			}
		}

		public static void Reset()
		{
			s_cache.Clear();
			s_warnedFormat = false;
			s_warnedOtherMod = false;
		}
	}
	public static class ContainerRegistry
	{
		private static readonly List<IStorageSource> s_sources = new List<IStorageSource>(256);

		private static readonly Dictionary<Container, ContainerSource> s_byContainer = new Dictionary<Container, ContainerSource>(256, ReferenceComparer<Container>.Instance);

		public static IReadOnlyList<IStorageSource> Sources => s_sources;

		public static int Count => s_sources.Count;

		public static void Register(Container c)
		{
			if (!((Object)(object)c == (Object)null) && !s_byContainer.ContainsKey(c))
			{
				ContainerSource containerSource = new ContainerSource(c);
				s_byContainer[c] = containerSource;
				s_sources.Add(containerSource);
			}
		}

		public static bool TryGet(Container c, out ContainerSource src)
		{
			src = null;
			if ((Object)(object)c != (Object)null)
			{
				return s_byContainer.TryGetValue(c, out src);
			}
			return false;
		}

		public static void Unregister(Container c)
		{
			if (!((Object)(object)c == (Object)null) && s_byContainer.TryGetValue(c, out var value))
			{
				s_byContainer.Remove(c);
				s_sources.Remove(value);
			}
		}

		public static void Compact()
		{
			int num = 0;
			for (int i = 0; i < s_sources.Count; i++)
			{
				ContainerSource containerSource = (ContainerSource)s_sources[i];
				if ((Object)(object)containerSource.Container == (Object)null)
				{
					s_byContainer.Remove(containerSource.Container);
				}
				else
				{
					s_sources[num++] = containerSource;
				}
			}
			if (num < s_sources.Count)
			{
				s_sources.RemoveRange(num, s_sources.Count - num);
			}
		}

		public static void Clear()
		{
			s_sources.Clear();
			s_byContainer.Clear();
			OwnershipRequests.Clear();
			DeferredTakes.Clear();
		}
	}
	public sealed class ContainerSource : IStorageSource
	{
		private const float RequestInterval = 2f;

		private static readonly Action<Container> s_save = Reflect.InstanceAction<Container>("Save");

		private static readonly Action<Container> s_load = Reflect.InstanceAction<Container>("Load");

		private static readonly Func<Container, long, bool> s_checkAccess = Reflect.InstanceFunc<Container, long, bool>("CheckAccess");

		private static readonly Func<InventoryGui, Container> s_currentContainer = Reflect.FieldGetter<InventoryGui, Container>("m_currentContainer");

		private static readonly List<ItemData> s_scratch = new List<ItemData>(16);

		private readonly Container _container;

		private ZNetView _nview;

		private string _prefabName;

		private readonly bool _isTombstone;

		private readonly bool _isVehicle;

		private float _lastRequest = float.NegativeInfinity;

		private int _playerBuilt = -1;

		private bool _accessOk;

		private float _accessCheckedAt = float.NegativeInfinity;

		private const float AccessRecheck = 3f;

		public static Func<ModSettings> SettingsProvider = () => Runtime.Settings;

		public Container Container => _container;

		private ZNetView NView
		{
			get
			{
				if ((Object)(object)_nview == (Object)null && (Object)(object)_container != (Object)null)
				{
					_nview = ((Component)_container).GetComponentInParent<ZNetView>();
				}
				return _nview;
			}
		}

		public bool IsVehicle
		{
			get
			{
				if ((Object)(object)_container != (Object)null)
				{
					return _isVehicle;
				}
				return false;
			}
		}

		public bool IsAlive
		{
			get
			{
				if ((Object)(object)_container == (Object)null)
				{
					return false;
				}
				ZNetView nView = NView;
				if ((Object)(object)nView != (Object)null && nView.IsValid())
				{
					return _container.GetInventory() != null;
				}
				return false;
			}
		}

		public bool IsUsable
		{
			get
			{
				//IL_010a: Unknown result type (might be due to invalid IL or missing references)
				if (!IsAlive || _isTombstone)
				{
					return false;
				}
				ModSettings modSettings = SettingsProvider();
				ZDO zDO = NView.GetZDO();
				if (modSettings.OnlyPlayerBuilt)
				{
					if (_playerBuilt < 0)
					{
						_playerBuilt = ((zDO.GetLong("creator", 0L) != 0) ? 1 : 0);
					}
					if (_playerBuilt == 0)
					{
						return false;
					}
				}
				if (!modSettings.IncludeVehicles && _isVehicle)
				{
					return false;
				}
				if (modSettings.SkipContainersInUse && !IsOpenByLocalPlayer() && (zDO.GetInt("InUse", 0) == 1 || _container.IsInUse()))
				{
					return false;
				}
				float time = Time.time;
				if (time - _accessCheckedAt >= 3f || time < _accessCheckedAt)
				{
					_accessCheckedAt = time;
					Player localPlayer = Player.m_localPlayer;
					long arg = (((Object)(object)localPlayer != (Object)null) ? localPlayer.GetPlayerID() : 0);
					_accessOk = s_checkAccess != null && s_checkAccess(_container, arg) && (!modSettings.RespectWards || PrivateArea.CheckAccess(((Component)_container).transform.position, 0f, false, false));
				}
				return _accessOk;
			}
		}

		public bool IsReady
		{
			get
			{
				if (IsAlive)
				{
					return NView.IsOwner();
				}
				return false;
			}
		}

		private bool Reachable
		{
			get
			{
				if (IsReady)
				{
					return true;
				}
				if (!SettingsProvider().TakeOwnership)
				{
					return false;
				}
				if (IsVehicle)
				{
					return !NView.GetZDO().HasOwner();
				}
				return true;
			}
		}

		public Vec3 Position
		{
			get
			{
				//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_0017: 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)
				Vector3 position = ((Component)_container).transform.position;
				return new Vec3(position.x, position.y, position.z);
			}
		}

		public string PrefabName
		{
			get
			{
				if (_prefabName != null)
				{
					return _prefabName;
				}
				ZNetView nView = NView;
				return _prefabName = Utils.GetPrefabName(((Object)(object)nView != (Object)null) ? ((Component)nView).gameObject : ((Component)_container).gameObject);
			}
		}

		public uint Revision
		{
			get
			{
				ZNetView nView = NView;
				if (!((Object)(object)nView != (Object)null) || !nView.IsValid())
				{
					return 0u;
				}
				return nView.GetZDO().DataRevision;
			}
		}

		public ContainerSource(Container container)
		{
			_container = container;
			_isTombstone = (Object)(object)((Component)container).GetComponent<TombStone>() != (Object)null;
			_isVehicle = (Object)(object)((Component)container).GetComponent<ZNetView>() == (Object)null;
		}

		public void Prepare()
		{
			Request(force: false);
		}

		private void Request(bool force)
		{
			if (!IsAlive || IsReady || !SettingsProvider().TakeOwnership || !IsUsable)
			{
				return;
			}
			ZNetView nView = NView;
			if (!nView.GetZDO().HasOwner())
			{
				nView.ClaimOwnership();
			}
			else
			{
				if (IsVehicle)
				{
					return;
				}
				float time = Time.time;
				if ((!OwnershipRequests.IsPending(_container) || !(time - _lastRequest < 2f)) && (force || !(time - _lastRequest < 2f)))
				{
					Player localPlayer = Player.m_localPlayer;
					if (!((Object)(object)localPlayer == (Object)null))
					{
						_lastRequest = time;
						OwnershipRequests.Add(_container);
						nView.InvokeRPC("RPC_RequestStack", new object[1] { localPlayer.GetPlayerID() });
						Perf.OwnershipRequests++;
						Log.Debug($"Asked the owner for {PrefabName} at {Position}");
					}
				}
			}
		}

		private bool IsOpenByLocalPlayer()
		{
			InventoryGui instance = InventoryGui.instance;
			if ((Object)(object)instance != (Object)null && s_currentContainer != null)
			{
				return (Object)(object)s_currentContainer(instance) == (Object)(object)_container;
			}
			return false;
		}

		private static bool Matches(ItemData it, string itemName, int quality)
		{
			if (it?.m_shared != null && it.m_shared.m_name == itemName && (quality < 0 || it.m_quality == quality))
			{
				return it.m_worldLevel >= Game.m_worldLevel;
			}
			return false;
		}

		public int Count(string itemName, int quality)
		{
			Inventory inventory = _container.GetInventory();
			if (inventory == null || !Reachable)
			{
				return 0;
			}
			List<ItemData> allItems = inventory.GetAllItems();
			int num = 0;
			for (int i = 0; i < allItems.Count; i++)
			{
				if (Matches(allItems[i], itemName, quality))
				{
					num += allItems[i].m_stack;
				}
			}
			return num;
		}

		private Inventory BeginEdit()
		{
			if (!IsReady)
			{
				return null;
			}
			try
			{
				s_load?.Invoke(_container);
			}
			catch (Exception ex)
			{
				Log.Debug("Container.Load failed: " + ex.Message);
			}
			return _container.GetInventory();
		}

		private void EndEdit()
		{
			try
			{
				s_save?.Invoke(_container);
			}
			catch (Exception ex)
			{
				Log.Debug("Container.Save failed: " + ex.Message);
			}
		}

		public int Take(string itemName, int amount, int quality)
		{
			if (amount <= 0)
			{
				return 0;
			}
			Inventory val = BeginEdit();
			if (val == null)
			{
				return 0;
			}
			s_scratch.Clear();
			List<ItemData> allItems = val.GetAllItems();
			for (int i = 0; i < allItems.Count; i++)
			{
				if (Matches(allItems[i], itemName, quality))
				{
					s_scratch.Add(allItems[i]);
				}
			}
			s_scratch.Sort((ItemData a, ItemData b) => a.m_stack.CompareTo(b.m_stack));
			int num = 0;
			for (int num2 = 0; num2 < s_scratch.Count; num2++)
			{
				if (num >= amount)
				{
					break;
				}
				ItemData val2 = s_scratch[num2];
				int num3 = Math.Min(val2.m_stack, amount - num);
				if (num3 > 0 && val.RemoveItem(val2, num3))
				{
					num += num3;
				}
			}
			s_scratch.Clear();
			if (num > 0)
			{
				EndEdit();
				Log.Debug($"Took {num}x {itemName} from {PrefabName} at {Position}");
			}
			return num;
		}

		public int TakeLater(string itemName, int amount, int quality)
		{
			if (amount <= 0 || !IsAlive || IsReady || !Reachable)
			{
				return 0;
			}
			int num = Math.Min(amount, Count(itemName, quality) - DeferredTakes.Reserved(this, itemName, quality));
			if (num <= 0)
			{
				return 0;
			}
			Request(force: true);
			if (IsReady)
			{
				return Take(itemName, num, quality);
			}
			DeferredTakes.Add(this, itemName, quality, num);
			Log.Debug($"Reserved {num}x {itemName} in {PrefabName} at {Position} until its owner hands it over");
			return num;
		}

		public ItemData TakeOne(string itemName)
		{
			Inventory val = BeginEdit();
			if (val == null)
			{
				return null;
			}
			ItemData val2 = null;
			List<ItemData> allItems = val.GetAllItems();
			for (int i = 0; i < allItems.Count; i++)
			{
				if (Matches(allItems[i], itemName, -1) && (val2 == null || allItems[i].m_stack < val2.m_stack))
				{
					val2 = allItems[i];
				}
			}
			if (val2 == null)
			{
				return null;
			}
			ItemData val3 = val2.Clone();
			val3.m_stack = 1;
			if (!val.RemoveItem(val2, 1))
			{
				return null;
			}
			EndEdit();
			Log.Debug($"Took 1x {itemName} from {PrefabName} at {Position} for a station");
			return val3;
		}

		public bool PutBack(ItemData item)
		{
			Inventory val = BeginEdit();
			if (val == null || !val.AddItem(item))
			{
				return false;
			}
			EndEdit();
			return true;
		}
	}
	public static class CookingTimers
	{
		private sealed class Watch
		{
			public float Cooked = -1f;

			public float ChangedAt;
		}

		private struct Cached
		{
			public uint DataRevision;

			public int SettingsRevision;

			public string Base;

			public string Text;

			public float At;
		}

		private const int NotDone = 0;

		private const int Done = 1;

		private const int Burnt = 2;

		private const float PausedAfter = 2.5f;

		private static readonly FieldRef<CookingStation, ZNetView> s_nview = AccessTools.FieldRefAccess<CookingStation, ZNetView>("m_nview");

		private static readonly string[] s_slotKeys = new string[16];

		private static readonly string[] s_statusKeys = new string[16];

		private static readonly Dictionary<CookingStation, Watch[]> s_watch = new Dictionary<CookingStation, Watch[]>(ReferenceComparer<CookingStation>.Instance);

		private static readonly StringBuilder s_sb = new StringBuilder(256);

		private const float RefreshEvery = 0.5f;

		private static readonly Dictionary<CookingStation, Cached> s_cache = new Dictionary<CookingStation, Cached>(ReferenceComparer<CookingStation>.Instance);

		public static bool Active
		{
			get
			{
				if (Runtime.Settings.Enabled)
				{
					return Runtime.Settings.ShowCookingTimers;
				}
				return false;
			}
		}

		public static void Reset()
		{
			s_watch.Clear();
			s_cache.Clear();
		}

		private static string SlotKey(int i)
		{
			if (i >= s_slotKeys.Length)
			{
				return "slot" + i;
			}
			return s_slotKeys[i] ?? (s_slotKeys[i] = "slot" + i);
		}

		private static string StatusKey(int i)
		{
			if (i >= s_statusKeys.Length)
			{
				return "slotstatus" + i;
			}
			return s_statusKeys[i] ?? (s_statusKeys[i] = "slotstatus" + i);
		}

		public static string Append(CookingStation station, string text)
		{
			if (!Active || (Object)(object)station == (Object)null || station.m_slots == null)
			{
				return text;
			}
			ZNetView val = s_nview.Invoke(station);
			if ((Object)(object)val == (Object)null || !val.IsValid())
			{
				return text;
			}
			ZDO zDO = val.GetZDO();
			float time = Time.time;
			ModSettings settings = Runtime.Settings;
			if (s_cache.TryGetValue(station, out var value) && value.DataRevision == zDO.DataRevision && value.SettingsRevision == settings.Revision && time - value.At < 0.5f && string.Equals(value.Base, text, StringComparison.Ordinal))
			{
				return value.Text;
			}
			string text2 = Build(station, zDO, text, time, settings);
			if (s_cache.Count > 256)
			{
				s_cache.Clear();
			}
			s_cache[station] = new Cached
			{
				DataRevision = zDO.DataRevision,
				SettingsRevision = settings.Revision,
				Base = text,
				Text = text2,
				At = time
			};
			return text2;
		}

		private static string Build(CookingStation station, ZDO zdo, string text, float now, ModSettings s)
		{
			Watch[] array = WatchFor(station);
			s_sb.Length = 0;
			for (int i = 0; i < station.m_slots.Length; i++)
			{
				string text2 = zdo.GetString(SlotKey(i), "");
				if (string.IsNullOrEmpty(text2))
				{
					continue;
				}
				float num = zdo.GetFloat(SlotKey(i), 0f);
				int num2 = zdo.GetInt(StatusKey(i), 0);
				Watch watch = ((i < array.Length) ? array[i] : null);
				bool flag = false;
				if (watch != null)
				{
					if (Math.Abs(num - watch.Cooked) > 0.001f)
					{
						watch.Cooked = num;
						watch.ChangedAt = now;
					}
					else
					{
						flag = now - watch.ChangedAt > 2.5f;
					}
				}
				ItemConversion val = Conversion(station, text2);
				if (num2 == 2 || val == null)
				{
					s_sb.Append('\n').AppendFormat(s.CookingBurntFormat, ItemName(station.m_overCookedItem, text2));
					continue;
				}
				string arg = ItemName(val.m_to, text2);
				if (num2 == 1)
				{
					if (!station.m_canOvercookItems || !s.CookingShowBurnTime)
					{
						s_sb.Append('\n').AppendFormat(s.CookingDoneFormat, arg);
					}
					else
					{
						s_sb.Append('\n').AppendFormat(s.CookingBurnsFormat, arg, Clock(val.m_cookTime * 2f - num));
					}
				}
				else
				{
					s_sb.Append('\n').AppendFormat(s.CookingReadyInFormat, arg, Clock(val.m_cookTime - num));
				}
				if (flag)
				{
					s_sb.Append(' ').Append(s.CookingPausedText);
				}
			}
			if (s_sb.Length == 0)
			{
				return text;
			}
			return text + s_sb.ToString();
		}

		private static Watch[] WatchFor(CookingStation station)
		{
			if (!s_watch.TryGetValue(station, out var value) || value.Length != station.m_slots.Length)
			{
				if (s_watch.Count > 256)
				{
					s_watch.Clear();
				}
				value = new Watch[station.m_slots.Length];
				for (int i = 0; i < value.Length; i++)
				{
					value[i] = new Watch
					{
						ChangedAt = Time.time
					};
				}
				s_watch[station] = value;
			}
			return value;
		}

		private static ItemConversion Conversion(CookingStation station, string itemName)
		{
			foreach (ItemConversion item in station.m_conversion)
			{
				if (((Object)(object)item.m_from != (Object)null && ((Object)((Component)item.m_from).gameObject).name == itemName) || ((Object)(object)item.m_to != (Object)null && ((Object)((Component)item.m_to).gameObject).name == itemName))
				{
					return item;
				}
			}
			return null;
		}

		private static string ItemName(ItemDrop drop, string fallback)
		{
			string text = (((Object)(object)drop != (Object)null) ? drop.m_itemData.m_shared.m_name : null);
			if (!string.IsNullOrEmpty(text))
			{
				return Localization.instance.Localize(text);
			}
			return fallback;
		}

		public static string Clock(float seconds)
		{
			int num = Math.Max(0, (int)Math.Ceiling(seconds));
			return num / 60 + ":" + (num % 60).ToString("00");
		}
	}
	public static class DeferredTakes
	{
		private sealed class Entry
		{
			public ContainerSource Source;

			public string Name;

			public int Quality;

			public int Amount;

			public float Deadline;

			public bool Denied;
		}

		public const float Timeout = 5f;

		private static readonly List<Entry> s_entries = new List<Entry>();

		public static int Pending => s_entries.Count;

		public static void Add(ContainerSource source, string itemName, int quality, int amount)
		{
			s_entries.Add(new Entry
			{
				Source = source,
				Name = itemName,
				Quality = quality,
				Amount = amount,
				Deadline = Time.time + 5f
			});
		}

		public static int Reserved(ContainerSource source, string itemName, int quality)
		{
			int num = 0;
			foreach (Entry s_entry in s_entries)
			{
				if (s_entry.Source == source && s_entry.Name == itemName && (quality < 0 || s_entry.Quality < 0 || s_entry.Quality == quality))
				{
					num += s_entry.Amount;
				}
			}
			return num;
		}

		public static void OnDenied(Container container)
		{
			foreach (Entry s_entry in s_entries)
			{
				if (s_entry.Source.Container == container)
				{
					s_entry.Denied = true;
				}
			}
		}

		public static void Tick()
		{
			for (int num = s_entries.Count - 1; num >= 0; num--)
			{
				Entry entry = s_entries[num];
				if (entry.Source.IsAlive && entry.Source.IsReady)
				{
					entry.Amount -= entry.Source.Take(entry.Name, entry.Amount, entry.Quality);
					if (entry.Amount > 0)
					{
						Fallback(entry);
					}
				}
				else
				{
					if (!entry.Denied && entry.Source.IsAlive && !(Time.time > entry.Deadline))
					{
						continue;
					}
					Fallback(entry);
				}
				s_entries.RemoveAt(num);
			}
		}

		private static void Fallback(Entry e)
		{
			int num = Runtime.TakeFromOwnContainers(e.Name, e.Amount, e.Quality);
			if (num < e.Amount)
			{
				Log.Warn($"Could only collect {num}/{e.Amount} {e.Name}: a chest near another player was opened before it could be used.");
			}
		}

		public static void Clear()
		{
			s_entries.Clear();
		}
	}
	public static class ItemCatalog
	{
		private static readonly Dictionary<string, string> s_prefabByName = new Dictionary<string, string>(StringComparer.Ordinal);

		private static ObjectDB s_builtFor;

		private static int s_builtCount = -1;

		private static readonly string[] s_bossTrophies = new string[6] { "eikthyr", "elder", "bonemass", "dragonqueen", "goblinking", "seekerqueen" };

		public static bool EnsureBuilt(PullRules rules)
		{
			ObjectDB instance = ObjectDB.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return false;
			}
			if ((Object)(object)instance == (Object)(object)s_builtFor && instance.m_items.Count == s_builtCount)
			{
				return false;
			}
			s_prefabByName.Clear();
			foreach (GameObject item in instance.m_items)
			{
				if (!((Object)(object)item == (Object)null))
				{
					string text = item.GetComponent<ItemDrop>()?.m_itemData?.m_shared?.m_name;
					if (!string.IsNullOrEmpty(text) && !s_prefabByName.ContainsKey(text))
					{
						s_prefabByName[text] = Utils.GetPrefa