Decompiled source of QoL Options v1.0.1

ValheimPureQoLModPack.dll

Decompiled 17 hours ago
using System;
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.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.UI;
using Valheim.SettingsGui;
using ValheimPureQoLModPack.Patches;
using ValheimPureQoLModPack.Utils;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("assembly_guiutils")]
[assembly: IgnoresAccessChecksTo("assembly_utils")]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("ValheimPureQoLModPack")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Pure quality-of-life mods for Valheim")]
[assembly: AssemblyFileVersion("1.0.1.0")]
[assembly: AssemblyInformationalVersion("1.0.1+5bfb53b6cbed52150e7386a5b80b941afb05dc87")]
[assembly: AssemblyProduct("ValheimPureQoLModPack")]
[assembly: AssemblyTitle("ValheimPureQoLModPack")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.1.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ValheimPureQoLModPack
{
	[BepInPlugin("com.lorthan.valheimpureqolmodpack", "ValheimPureQoLModPack", "1.0.1")]
	public class Plugin : BaseUnityPlugin
	{
		public const string PluginGUID = "com.lorthan.valheimpureqolmodpack";

		public const string PluginName = "ValheimPureQoLModPack";

		public const string PluginVersion = "1.0.1";

		private readonly Harmony _harmony = new Harmony("com.lorthan.valheimpureqolmodpack");

		private HashSet<ConfigDefinition> _savedBeforeBind;

		public static Plugin Instance { get; private set; }

		public static ManualLogSource Log => ((BaseUnityPlugin)Instance).Logger;

		public static ConfigEntry<bool> ModEnabled { get; private set; }

		public static ConfigEntry<float> ZoomOutOnFoot { get; private set; }

		public static ConfigEntry<float> ZoomOutMounted { get; private set; }

		public static ConfigEntry<float> ZoomOutBoat { get; private set; }

		public static ConfigEntry<bool> EquipmentInWater { get; private set; }

		public static ConfigEntry<bool> BatchDepositEnabled { get; private set; }

		public static ConfigEntry<KeyCode> BatchModifierKey { get; private set; }

		public static ConfigEntry<bool> ShowTooltipHint { get; private set; }

		public static ConfigEntry<bool> AutoPullFromChests { get; private set; }

		public static ConfigEntry<float> AutoPullRange { get; private set; }

		public static ConfigEntry<bool> QuickStackEnabled { get; private set; }

		public static ConfigEntry<float> QuickStackRange { get; private set; }

		public static ConfigEntry<bool> QuickStackOnlyExistingStacks { get; private set; }

		public static ConfigEntry<bool> SortEnabled { get; private set; }

		public static ConfigEntry<bool> RestockEnabled { get; private set; }

		public static ConfigEntry<bool> TrashEnabled { get; private set; }

		public static ConfigEntry<bool> EquipmentSlots { get; private set; }

		public static ConfigEntry<float> RestockRange { get; private set; }

		public static ConfigEntry<KeyCode> QuickStackKey { get; private set; }

		public static ConfigEntry<KeyCode> RestockKey { get; private set; }

		public static ConfigEntry<KeyCode> SortKey { get; private set; }

		public static ConfigEntry<bool> CraftingQueueEnabled { get; private set; }

		public static ConfigEntry<float> QueueButtonOffsetX { get; private set; }

		public static ConfigEntry<float> QueueButtonOffsetY { get; private set; }

		public static ConfigEntry<bool> CraftingSearchEnabled { get; private set; }

		public static ConfigEntry<bool> RepairAll { get; private set; }

		public static ConfigEntry<bool> StickyAttack { get; private set; }

		public static ConfigEntry<bool> StickyBlock { get; private set; }

		public static ConfigEntry<KeyCode> StickyModifierKey { get; private set; }

		public static ConfigEntry<bool> PlayerStatsEnabled { get; private set; }

		public static ConfigEntry<float> PlayerStatsButtonOffsetX { get; private set; }

		public static ConfigEntry<float> PlayerStatsButtonOffsetY { get; private set; }

		public static ConfigEntry<bool> NetInfoEnabled { get; private set; }

		public static ConfigEntry<bool> NetInfoShowPing { get; private set; }

		public static ConfigEntry<bool> NetInfoShowServerDelay { get; private set; }

		public static ConfigEntry<bool> NetInfoShowPacketLoss { get; private set; }

		public static ConfigEntry<bool> NetInfoShowNearbyPlayers { get; private set; }

		public static ConfigEntry<bool> NetInfoShowCreatureOwner { get; private set; }

		public static ConfigEntry<int> NetInfoPingWarnMs { get; private set; }

		public static ConfigEntry<int> NetInfoPingBadMs { get; private set; }

		public static ConfigEntry<int> NetInfoServerDelayMinMs { get; private set; }

		public static ConfigEntry<float> NetInfoPacketLossMinPercent { get; private set; }

		public static ConfigEntry<float> NetInfoFontSize { get; private set; }

		public static ConfigEntry<float> NetInfoOffsetX { get; private set; }

		public static ConfigEntry<float> NetInfoOffsetY { get; private set; }

		public static ConfigEntry<bool> AutoEatEnabled { get; private set; }

		public static ConfigEntry<int> AutoEatThresholdPercent { get; private set; }

		public static ConfigEntry<bool> CheatLowStaminaUse { get; private set; }

		public static ConfigEntry<bool> CheatLowEitrUse { get; private set; }

		public static ConfigEntry<float> CheatSkillGainMultiplier { get; private set; }

		public static ConfigEntry<bool> CheatNeverOverweight { get; private set; }

		public static ConfigEntry<float> CheatDropChanceMultiplier { get; private set; }

		public static ConfigEntry<float> CheatTrophyChanceMultiplier { get; private set; }

		public static ConfigEntry<float> CheatSpawnRateMultiplier { get; private set; }

		public static ConfigEntry<float> CheatSpawnPopulationMultiplier { get; private set; }

		public static ConfigEntry<float> CheatSpawnGroupSizeMultiplier { get; private set; }

		public static ConfigEntry<bool> DeconstructToInventory { get; private set; }

		public static ConfigEntry<bool> RemovableCarts { get; private set; }

		public static ConfigEntry<bool> RemovableShips { get; private set; }

		public static ConfigEntry<bool> AreaRepair { get; private set; }

		public static ConfigEntry<float> AreaRepairRadius { get; private set; }

		public static ConfigEntry<bool> BuildUndo { get; private set; }

		public static ConfigEntry<KeyCode> UndoKey { get; private set; }

		public static ConfigEntry<KeyCode> UndoModifierKey { get; private set; }

		public static ConfigEntry<bool> SeasonalItemsAllYear { get; private set; }

		public static ConfigEntry<bool> StabilityPreview { get; private set; }

		public static ConfigEntry<TimeOfDayMode> TimeOfDay { get; private set; }

		public static ConfigEntry<int> DaylightPercent { get; private set; }

		public static ConfigEntry<WeatherMode> Weather { get; private set; }

		public static ConfigEntry<bool> NoMistyWeather { get; private set; }

		public static ConfigEntry<bool> WindOverride { get; private set; }

		public static ConfigEntry<float> WindDirection { get; private set; }

		public static ConfigEntry<float> WindStrength { get; private set; }

		private void Awake()
		{
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Expected O, but got Unknown
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Expected O, but got Unknown
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Expected O, but got Unknown
			//IL_0694: Unknown result type (might be due to invalid IL or missing references)
			//IL_069e: Expected O, but got Unknown
			//IL_0711: Unknown result type (might be due to invalid IL or missing references)
			//IL_071b: Expected O, but got Unknown
			//IL_076e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0778: Expected O, but got Unknown
			//IL_07ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_07b5: Expected O, but got Unknown
			//IL_07e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_07f2: Expected O, but got Unknown
			//IL_0825: Unknown result type (might be due to invalid IL or missing references)
			//IL_082f: Expected O, but got Unknown
			//IL_0862: Unknown result type (might be due to invalid IL or missing references)
			//IL_086c: Expected O, but got Unknown
			//IL_091f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0929: Expected O, but got Unknown
			//IL_0a18: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a22: Expected O, but got Unknown
			//IL_0ab5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0abf: Expected O, but got Unknown
			//IL_0af2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0afc: Expected O, but got Unknown
			//IL_0c4c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0c56: Expected O, but got Unknown
			//IL_0daa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0db4: Expected O, but got Unknown
			//IL_0d78: Unknown result type (might be due to invalid IL or missing references)
			//IL_0d82: Expected O, but got Unknown
			Instance = this;
			_savedBeforeBind = new HashSet<ConfigDefinition>(OrphanedEntries().Keys);
			ModEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("1. General", "Enabled", true, "Enable or disable this mod.");
			ZoomOutOnFoot = ((BaseUnityPlugin)this).Config.Bind<float>("1. General", "ZoomOutOnFoot", 1f, new ConfigDescription("How far the camera can zoom out on foot, as a multiple of the game's limit. 1 is the normal game.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 4f), Array.Empty<object>()));
			ZoomOutMounted = ((BaseUnityPlugin)this).Config.Bind<float>("1. General", "ZoomOutMounted", 1f, new ConfigDescription("How far the camera can zoom out while riding, as a multiple of the game's on-foot limit (the game uses that one for riding too). 1 is the normal game.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 4f), Array.Empty<object>()));
			ZoomOutBoat = ((BaseUnityPlugin)this).Config.Bind<float>("1. General", "ZoomOutBoat", 1f, new ConfigDescription("How far the camera can zoom out while steering a ship, as a multiple of the game's steering limit. 1 is the normal game.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 4f), Array.Empty<object>()));
			EquipmentInWater = ((BaseUnityPlugin)this).Config.Bind<bool>("1. General", "EquipmentInWater", true, "While swimming, weapons, tools, shields and torches stay in your hands and can be equipped and used. Swimming still drains stamina as usual.");
			BatchDepositEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("2. Chests & Stations", "BatchDepositEnabled", true, "Holding the modifier key while pressing E fills a smelter, kiln, fire or cooking station in one go.");
			BatchModifierKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("2. Chests & Stations", "BatchDepositModifierKey", (KeyCode)304, "Key to hold while pressing E to batch deposit into Smelters, Fireplaces, Torches, etc.");
			ShowTooltipHint = ((BaseUnityPlugin)this).Config.Bind<bool>("2. Chests & Stations", "ShowTooltipHint", true, "Show the batch deposit hotkey hint in hover text prompts.");
			AutoPullFromChests = ((BaseUnityPlugin)this).Config.Bind<bool>("2. Chests & Stations", "AutoPullFromChests", true, "Enable pulling fuel and materials from nearby containers/chests.");
			AutoPullRange = ((BaseUnityPlugin)this).Config.Bind<float>("2. Chests & Stations", "AutoPullRange", 10f, "Radius in meters around the structure to search for nearby containers.");
			QuickStackEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("2. Chests & Stations", "QuickStackEnabled", true, "Add a Quick Stack button to the inventory that sends items to nearby chests.");
			QuickStackRange = ((BaseUnityPlugin)this).Config.Bind<float>("2. Chests & Stations", "QuickStackRange", 10f, "Radius in meters around the player to search for chests to quick-stack into.");
			QuickStackOnlyExistingStacks = ((BaseUnityPlugin)this).Config.Bind<bool>("2. Chests & Stations", "QuickStackOnlyExistingStacks", true, "Only quick-stack an item into chests that already contain that item type, instead of any accessible chest.");
			SortEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("2. Chests & Stations", "SortEnabled", true, "Sort buttons for your inventory (under Restock; the hotbar row stays as it is) and for the open chest. Partial stacks are merged and items are laid out by kind, then by name.");
			RestockEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("2. Chests & Stations", "RestockEnabled", true, "A Restock button in the inventory that fills up the partial ammo, food and potion stacks you carry from nearby chests (the open one included).");
			RestockRange = ((BaseUnityPlugin)this).Config.Bind<float>("2. Chests & Stations", "RestockRange", 10f, "Radius in meters around the player to search for chests to restock from.");
			QuickStackKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("2. Chests & Stations", "QuickStackKey", (KeyCode)113, "Key that quick-stacks into nearby chests while the inventory is open. None turns it off.");
			RestockKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("2. Chests & Stations", "RestockKey", (KeyCode)114, "Key that restocks from nearby chests while the inventory is open. None turns it off.");
			SortKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("2. Chests & Stations", "SortKey", (KeyCode)115, "Key that sorts while the inventory is open: the open chest, or your inventory when no chest is open. None turns it off.");
			TrashEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("2. Chests & Stations", "TrashEnabled", true, "A trash slot under the armor value in the inventory: drop an item on it and confirm to delete it. Hold Shift to skip the confirmation.");
			EquipmentSlots = ((BaseUnityPlugin)this).Config.Bind<bool>("2. Chests & Stations", "EquipmentSlots", true, "Worn helmet, chest, legs, cape, utility item and trinket sit in their own panel beside the inventory instead of taking inventory space. Turning this off moves them back into the inventory, still worn; it needs free room for them, and without it the setting switches back on. Nothing is ever deleted. Before removing the mod, turn this off; otherwise the game drops the worn items at your feet on the next load.");
			CraftingQueueEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("3. Crafting", "QueueEnabled", true, "Add a Queue button at crafting stations to line up several crafts/upgrades that run one after another.");
			QueueButtonOffsetX = ((BaseUnityPlugin)this).Config.Bind<float>("3. Crafting", "QueueButtonOffsetX", 0f, "Horizontal offset in pixels of the Queue button, relative to its spot at the right of the Craft button (positive = right).");
			QueueButtonOffsetY = ((BaseUnityPlugin)this).Config.Bind<float>("3. Crafting", "QueueButtonOffsetY", 0f, "Vertical offset in pixels of the Queue button, relative to its spot at the right of the Craft button (positive = up).");
			CraftingSearchEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("3. Crafting", "SearchEnabled", true, "Add a search field above the recipe list of every crafting interface that shows only recipes whose name contains the text.");
			RepairAll = ((BaseUnityPlugin)this).Config.Bind<bool>("3. Crafting", "RepairAll", true, "Repair button at a crafting station repairs every repairable item at once instead of one at a time.");
			StickyModifierKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("4. Combat & Survival", "StickyModifierKey", (KeyCode)308, "Hold this key while clicking Attack or Block to keep it held until you click that button again. Plain clicks stay vanilla. Avoid LeftControl (crouch) and Tab (inventory).");
			StickyAttack = ((BaseUnityPlugin)this).Config.Bind<bool>("4. Combat & Survival", "StickyAttack", true, "Modifier + Attack keeps attacking as if the button were held (bows stay drawn) until the next Attack click.");
			StickyBlock = ((BaseUnityPlugin)this).Config.Bind<bool>("4. Combat & Survival", "StickyBlock", true, "Modifier + Block keeps blocking until the next Block click. Ignored while the game's own Toggle block setting is on.");
			PlayerStatsEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("5. Interface", "PlayerStatsEnabled", true, "Add a Stats button next to Skills/Trophies in the inventory, showing enemies killed and time alive since your last death. Kills and real time are counted from when this is enabled.");
			PlayerStatsButtonOffsetX = ((BaseUnityPlugin)this).Config.Bind<float>("5. Interface", "PlayerStatsButtonOffsetX", 0f, "Horizontal offset in pixels of the Stats button, relative to its spot below the vanilla Skills/Trophies buttons (positive = right).");
			PlayerStatsButtonOffsetY = ((BaseUnityPlugin)this).Config.Bind<float>("5. Interface", "PlayerStatsButtonOffsetY", 0f, "Vertical offset in pixels of the Stats button, relative to its spot below the vanilla Skills/Trophies buttons (positive = up).");
			NetInfoEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("5. Interface", "NetInfoEnabled", true, "Show a small line of network info under the minimap, to tell whether lag comes from your connection, the server or another player.");
			NetInfoShowPing = ((BaseUnityPlugin)this).Config.Bind<bool>("5. Interface", "ShowPing", true, "Show your ping to the server (when hosting: the average ping of the connected players). Hidden when playing alone.");
			NetInfoShowServerDelay = ((BaseUnityPlugin)this).Config.Bind<bool>("5. Interface", "ShowServerDelay", true, "Show how long the server takes to answer on top of the network time, when it reaches ServerDelayMinMs. High values mean the server itself is overloaded. Not shown while hosting.");
			NetInfoShowPacketLoss = ((BaseUnityPlugin)this).Config.Bind<bool>("5. Interface", "ShowPacketLoss", true, "Show the share of lost packets on your connection, when it reaches PacketLossMinPercent. Loss causes rubber-banding even with a low ping.");
			NetInfoShowNearbyPlayers = ((BaseUnityPlugin)this).Config.Bind<bool>("5. Interface", "ShowNearbyPlayers", true, "Show how many other players are close enough to share your area (the game's active area). Hidden when nobody is.");
			NetInfoShowCreatureOwner = ((BaseUnityPlugin)this).Config.Bind<bool>("5. Interface", "ShowEnemyOwner", true, "Show whose game runs most creatures around you. When it's another player, what you feel from those creatures is roughly your ping plus theirs. Only shown with players nearby.");
			NetInfoPingWarnMs = ((BaseUnityPlugin)this).Config.Bind<int>("5. Interface", "PingWarnMs", 120, "Ping in milliseconds from which it shows in yellow.");
			NetInfoPingBadMs = ((BaseUnityPlugin)this).Config.Bind<int>("5. Interface", "PingBadMs", 250, "Ping in milliseconds from which it shows in red. It also turns red while the game shows its bad connection warning.");
			NetInfoServerDelayMinMs = ((BaseUnityPlugin)this).Config.Bind<int>("5. Interface", "ServerDelayMinMs", 60, "Server delay in milliseconds from which it is shown (red from twice this).");
			NetInfoPacketLossMinPercent = ((BaseUnityPlugin)this).Config.Bind<float>("5. Interface", "PacketLossMinPercent", 1f, "Packet loss in percent from which it is shown (red from 5%).");
			NetInfoFontSize = ((BaseUnityPlugin)this).Config.Bind<float>("5. Interface", "NetInfoFontSize", 16f, "Text size of the network info line.");
			NetInfoOffsetX = ((BaseUnityPlugin)this).Config.Bind<float>("5. Interface", "NetInfoOffsetX", 0f, "Horizontal offset in pixels of the network info line, relative to its spot centered under the minimap (positive = right).");
			NetInfoOffsetY = ((BaseUnityPlugin)this).Config.Bind<float>("5. Interface", "NetInfoOffsetY", 0f, "Vertical offset in pixels of the network info line, relative to its spot under the minimap (positive = up).");
			AutoEatEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("4. Combat & Survival", "AutoEatEnabled", true, "When one of your foods is about to run out, eat the same food again from your inventory. Nothing happens if you don't carry that food.");
			AutoEatThresholdPercent = ((BaseUnityPlugin)this).Config.Bind<int>("4. Combat & Survival", "AutoEatThresholdPercent", 10, new ConfigDescription("Share of a food's time left when it gets eaten again. The game only allows eating a food again below half its time, so 50 is the most.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 50), Array.Empty<object>()));
			CheatLowStaminaUse = ((BaseUnityPlugin)this).Config.Bind<bool>("8. Cheats", "LowStaminaUse", false, "Running, jumping, swimming, attacking, blocking and dodging use only 0.01% of the normal stamina.");
			CheatLowEitrUse = ((BaseUnityPlugin)this).Config.Bind<bool>("8. Cheats", "LowEitrUse", false, "Staffs and other eitr users spend only 0.01% of the normal eitr.");
			CheatSkillGainMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("8. Cheats", "SkillGainMultiplier", 1f, new ConfigDescription("Multiplies the experience every skill gains. 1 is the normal game.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 10f), Array.Empty<object>()));
			CheatNeverOverweight = ((BaseUnityPlugin)this).Config.Bind<bool>("8. Cheats", "NeverOverweight", false, "Carrying more than your weight limit never slows you down or drains stamina.");
			CheatDropChanceMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("8. Cheats", "DropChanceMultiplier", 1f, new ConfigDescription("Multiplies the chance of every creature drop except trophies (up to always dropping). 1 is the normal game. Only drops your game rolls: always when playing alone or hosting.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 20f), Array.Empty<object>()));
			CheatTrophyChanceMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("8. Cheats", "TrophyChanceMultiplier", 1f, new ConfigDescription("Multiplies the chance of creatures dropping their trophy (up to always dropping). 1 is the normal game. Only drops your game rolls: always when playing alone or hosting.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 20f), Array.Empty<object>()));
			CheatSpawnRateMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("8. Cheats", "SpawnRateMultiplier", 1f, new ConfigDescription("Multiplies how often creatures spawn in the open world, from nests and spawners, and at fixed spawn points such as crypts. Population limits stay the same and raids keep their normal rate. 1 is the normal game. Only areas your game runs: always when playing alone or hosting.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 10f), Array.Empty<object>()));
			CheatSpawnPopulationMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("8. Cheats", "SpawnPopulationMultiplier", 1f, new ConfigDescription("Multiplies how many of a creature an area of the open world or a nest/spawner can hold at once. Fixed spawn points such as crypts always hold one, and raids stay normal. 1 is the normal game. Only areas your game runs: always when playing alone or hosting.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 10f), Array.Empty<object>()));
			CheatSpawnGroupSizeMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("8. Cheats", "SpawnGroupSizeMultiplier", 1f, new ConfigDescription("Multiplies the size of creature packs spawning in the open world (wolves in packs of 1-4 come in 2-8 at 2). The area's creature limit grows with it so the bigger packs fit. Nests, fixed spawn points and raids stay normal. 1 is the normal game. Only areas your game runs: always when playing alone or hosting.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 5f), Array.Empty<object>()));
			DeconstructToInventory = ((BaseUnityPlugin)this).Config.Bind<bool>("6. Building", "DeconstructToInventory", true, "Materials from pieces you remove with the hammer go straight into your inventory instead of falling on the ground, even past your weight limit. What doesn't fit in a free slot still drops.");
			RemovableCarts = ((BaseUnityPlugin)this).Config.Bind<bool>("6. Building", "RemovableCarts", true, "Carts can be removed with the hammer like other pieces, giving their materials back. Nobody may be pulling the cart and its cargo must be empty.");
			RemovableShips = ((BaseUnityPlugin)this).Config.Bind<bool>("6. Building", "RemovableShips", true, "Ships can be removed with the hammer like other pieces, giving their materials back. Nobody may be aboard and the cargo must be empty.");
			AreaRepair = ((BaseUnityPlugin)this).Config.Bind<bool>("6. Building", "AreaRepair", true, "With the hammer's repair tool selected, hold Attack to repair every damaged piece around you. Each piece costs the same stamina and hammer wear as repairing it by hand. A plain click repairs one piece as usual.");
			AreaRepairRadius = ((BaseUnityPlugin)this).Config.Bind<float>("6. Building", "AreaRepairRadius", 10f, new ConfigDescription("Radius in meters around you that area repair covers.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(2f, 30f), Array.Empty<object>()));
			BuildUndo = ((BaseUnityPlugin)this).Config.Bind<bool>("6. Building", "BuildUndo", true, "While the hammer is out, the undo key (Ctrl+Z by default) reverses your last placements and removals, up to 3. Undoing a placement refunds its materials; undoing a removal puts the piece back and costs its materials again.");
			UndoKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("6. Building", "UndoKey", (KeyCode)122, "Key that undoes the last placement or removal (together with UndoModifierKey).");
			UndoModifierKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("6. Building", "UndoModifierKey", (KeyCode)306, "Key to hold together with UndoKey. None means UndoKey alone.");
			SeasonalItemsAllYear = ((BaseUnityPlugin)this).Config.Bind<bool>("6. Building", "SeasonalItemsAllYear", true, "Seasonal pieces and recipes (Yule, Halloween, Midsummer...) can be learned, built and crafted all year, with their normal materials, as during their season.");
			StabilityPreview = ((BaseUnityPlugin)this).Config.Bind<bool>("6. Building", "StabilityPreview", true, "While placing a piece, its ghost shows the support it would get there, in the colors the hammer shows on placed pieces (blue: full, green to red: weaker, red: it would fall).");
			TimeOfDay = ((BaseUnityPlugin)this).Config.Bind<TimeOfDayMode>("7. Time & Weather", "TimeOfDay", TimeOfDayMode.Normal, "Normal day/night cycle, always day (noon) or always night (midnight).");
			DaylightPercent = ((BaseUnityPlugin)this).Config.Bind<int>("7. Time & Weather", "DaylightPercent", 70, new ConfigDescription("Share of the 20-minute day/night cycle that is day; the game's is 70. Higher means longer days and shorter nights. The clock and day count don't change.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(10, 95), Array.Empty<object>()));
			Weather = ((BaseUnityPlugin)this).Config.Bind<WeatherMode>("7. Time & Weather", "Weather", WeatherMode.Normal, "Force a weather. Boss fights, raids, dungeons and the Mistlands keep their own.");
			NoMistyWeather = ((BaseUnityPlugin)this).Config.Bind<bool>("7. Time & Weather", "NoMistyWeather", false, "Misty weather (such as in the Meadows and Black Forest) never comes up. The Mistlands' mist is not affected.");
			WindOverride = ((BaseUnityPlugin)this).Config.Bind<bool>("7. Time & Weather", "WindOverride", false, "Use a fixed wind direction and strength instead of the changing wind. Also drives sailing.");
			WindDirection = ((BaseUnityPlugin)this).Config.Bind<float>("7. Time & Weather", "WindDirection", 0f, new ConfigDescription("Compass direction the fixed wind blows toward, in degrees (0 = north, 90 = east).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 345f), Array.Empty<object>()));
			WindStrength = ((BaseUnityPlugin)this).Config.Bind<float>("7. Time & Weather", "WindStrength", 50f, new ConfigDescription("Strength of the fixed wind, in percent.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			MigrateOldSetting((ConfigEntryBase)(object)CraftingQueueEnabled, "6. Crafting Queue", "Enabled");
			MigrateOldSetting((ConfigEntryBase)(object)QueueButtonOffsetX, "6. Crafting Queue", "ButtonOffsetX");
			MigrateOldSetting((ConfigEntryBase)(object)QueueButtonOffsetY, "6. Crafting Queue", "ButtonOffsetY");
			MigrateOldSetting((ConfigEntryBase)(object)BatchDepositEnabled, "2. Batch Deposit", "Enabled");
			MigrateOldSetting((ConfigEntryBase)(object)BatchModifierKey, "2. Batch Deposit", "ModifierKey");
			MigrateOldSetting((ConfigEntryBase)(object)QuickStackEnabled, "4. Quick Stack", "Enabled");
			MigrateOldSetting((ConfigEntryBase)(object)PlayerStatsEnabled, "8. Player Stats", "Enabled");
			MigrateOldSetting((ConfigEntryBase)(object)PlayerStatsButtonOffsetX, "8. Player Stats", "ButtonOffsetX");
			MigrateOldSetting((ConfigEntryBase)(object)PlayerStatsButtonOffsetY, "8. Player Stats", "ButtonOffsetY");
			MigrateOldSetting((ConfigEntryBase)(object)NetInfoEnabled, "9. Network Info", "Enabled");
			MigrateOldSetting((ConfigEntryBase)(object)NetInfoFontSize, "9. Network Info", "FontSize");
			MigrateOldSetting((ConfigEntryBase)(object)NetInfoOffsetX, "9. Network Info", "OffsetX");
			MigrateOldSetting((ConfigEntryBase)(object)NetInfoOffsetY, "9. Network Info", "OffsetY");
			MigrateOldSetting((ConfigEntryBase)(object)AutoEatEnabled, "10. Auto Eat", "Enabled");
			MigrateOldSetting((ConfigEntryBase)(object)AutoEatThresholdPercent, "10. Auto Eat", "ThresholdPercent");
			OrphanedEntries().Remove(new ConfigDefinition("4. Quick Stack", "QuickStackKey"));
			MigrateSection("2. Chests & Stations", "2. Batch Deposit", "3. Nearby Chests", "4. Quick Stack");
			MigrateSection("3. Crafting", "5. Repair", "6. Crafting");
			MigrateSection("4. Combat & Survival", "7. Combat");
			MigrateSection("5. Interface", "9. Network Info");
			MigrateSection("6. Building", "12. Building");
			MigrateSection("7. Time & Weather", "13. Time & Weather");
			MigrateSection("8. Cheats", "11. Cheats");
			string[] array = new string[2] { "2. Chests & Stations", "4. Quick Stack" };
			foreach (string text in array)
			{
				string[] array2 = new string[4] { "QuickStackButtonOffsetX", "QuickStackButtonOffsetY", "ButtonOffsetX", "ButtonOffsetY" };
				foreach (string text2 in array2)
				{
					OrphanedEntries().Remove(new ConfigDefinition(text, text2));
				}
			}
			OrphanedEntries().Remove(new ConfigDefinition("11. Cheats", "SeasonalItemsAllYear"));
			((BaseUnityPlugin)this).Config.Save();
			if (!ModEnabled.Value)
			{
				_harmony.PatchAll(typeof(SettingsPatches.InitializeTabsPatch));
				_harmony.PatchAll(typeof(LocalizationPatches));
				_harmony.PatchAll(typeof(EquipmentSlotsPatches.SetInventorySizePatch));
				ModLocalization.Register(Localization.instance, Localization.instance.GetSelectedLanguage());
				Log.LogInfo((object)"ValheimPureQoLModPack is disabled via configuration.");
			}
			else
			{
				Game.isModded = false;
				_harmony.PatchAll();
				ModLocalization.Register(Localization.instance, Localization.instance.GetSelectedLanguage());
				Log.LogInfo((object)"ValheimPureQoLModPack v1.0.1 initialized successfully!");
			}
		}

		private Dictionary<ConfigDefinition, string> OrphanedEntries()
		{
			return (AccessTools.Property(typeof(ConfigFile), "OrphanedEntries")?.GetValue(((BaseUnityPlugin)this).Config) as Dictionary<ConfigDefinition, string>) ?? new Dictionary<ConfigDefinition, string>();
		}

		private void MigrateOldSetting(ConfigEntryBase entry, string oldSection, string oldKey)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Expected O, but got Unknown
			Dictionary<ConfigDefinition, string> dictionary = OrphanedEntries();
			ConfigDefinition key = new ConfigDefinition(oldSection, oldKey);
			if (dictionary.TryGetValue(key, out var value))
			{
				dictionary.Remove(key);
				if (!_savedBeforeBind.Contains(entry.Definition))
				{
					entry.SetSerializedValue(value);
					Log.LogInfo((object)("Moved setting [" + oldSection + "] " + oldKey + " to [" + entry.Definition.Section + "] " + entry.Definition.Key + "."));
				}
			}
		}

		private void MigrateSection(string section, params string[] oldSections)
		{
			List<ConfigEntryBase> list = new List<ConfigEntryBase>();
			foreach (KeyValuePair<ConfigDefinition, ConfigEntryBase> item in ((BaseUnityPlugin)this).Config)
			{
				if (item.Key.Section == section)
				{
					list.Add(item.Value);
				}
			}
			foreach (ConfigEntryBase item2 in list)
			{
				foreach (string oldSection in oldSections)
				{
					MigrateOldSetting(item2, oldSection, item2.Definition.Key);
				}
			}
		}

		private void OnDestroy()
		{
			_harmony.UnpatchSelf();
		}
	}
}
namespace ValheimPureQoLModPack.Utils
{
	public static class BatchDepositHelper
	{
		public static bool IsModifierActive()
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.ModEnabled.Value || !Plugin.BatchDepositEnabled.Value)
			{
				return false;
			}
			KeyCode value = Plugin.BatchModifierKey.Value;
			if ((int)value != 0)
			{
				return Input.GetKey(value);
			}
			return false;
		}

		public static void AppendTooltip(ref string hoverText, string actionKey = "qol_hint_addall", bool usesChests = true)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			if (Plugin.ModEnabled.Value && Plugin.BatchDepositEnabled.Value && Plugin.ShowTooltipHint.Value)
			{
				string text = ((object)Plugin.BatchModifierKey.Value/*cast due to .constrained prefix*/).ToString();
				string text2 = Localization.instance.Localize("$KEY_Use");
				string text3 = ((usesChests && Plugin.AutoPullFromChests.Value) ? (" " + ModLocalization.Get("qol_hint_inclchests")) : "");
				hoverText = hoverText + "\n[<color=yellow><b>" + text + "+" + text2 + "</b></color>] " + ModLocalization.Get(actionKey) + text3;
			}
		}

		public static void ShowLocalizedMessage(Humanoid user, MessageType type, string templateKey, string arg = null)
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)user == (Object)null) && !string.IsNullOrEmpty(templateKey))
			{
				string text2;
				if (!string.IsNullOrEmpty(arg))
				{
					string text = Localization.instance.Localize(arg);
					text2 = Localization.instance.Localize(templateKey, new string[1] { text });
				}
				else
				{
					text2 = Localization.instance.Localize(templateKey);
				}
				((Character)user).Message(type, text2, 0, (Sprite)null, false);
			}
		}

		public static bool TryDeposit(Humanoid user, Vector3 structurePos, string itemName, int currentAmount, int maxAmount, Action<int> sendRpcs, string fullMsgKey = "$msg_itsfull", string emptyMsgKey = "$msg_donthaveany", string successMsgKey = "$msg_added", bool useChests = true)
		{
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)user == (Object)null)
			{
				return false;
			}
			Inventory inventory = user.GetInventory();
			if (inventory == null)
			{
				return false;
			}
			int num = maxAmount - currentAmount;
			if (num <= 0)
			{
				ShowLocalizedMessage(user, (MessageType)2, fullMsgKey, itemName);
				return true;
			}
			bool flag = IsModifierActive();
			int num2 = inventory.CountItems(itemName, -1, true);
			if (!flag && num2 > 0)
			{
				return false;
			}
			List<Container> list = null;
			int num3 = 0;
			if (useChests && Plugin.AutoPullFromChests.Value)
			{
				Player val = (Player)(object)((user is Player) ? user : null);
				long playerID = ((val != null) ? val.GetPlayerID() : 0);
				list = ContainerHelper.GetNearbyContainers(structurePos, Plugin.AutoPullRange.Value, playerID);
				num3 = ContainerHelper.CountItemsInContainers(list, itemName);
			}
			int num4 = num2 + num3;
			if (num4 <= 0)
			{
				ShowLocalizedMessage(user, (MessageType)2, emptyMsgKey, itemName);
				return true;
			}
			int num5 = ((!flag) ? 1 : Mathf.Min(num, num4));
			if (num5 <= 0)
			{
				return true;
			}
			int num6 = Mathf.Min(num2, num5);
			if (num6 > 0)
			{
				inventory.RemoveItem(itemName, num6, -1, true);
			}
			int num7 = num5 - num6;
			if (num7 > 0 && list != null)
			{
				ContainerHelper.RemoveItemsFromContainers(list, itemName, num7);
			}
			sendRpcs?.Invoke(num5);
			string text = Localization.instance.Localize(itemName);
			string arg = (flag ? $"{text} x{num5}" : text);
			ShowLocalizedMessage(user, (MessageType)(flag ? 1 : 1), successMsgKey, arg);
			return true;
		}
	}
	public static class ContainerHelper
	{
		private static readonly List<Piece> _tempPieces = new List<Piece>();

		private static float _lastScanTime = -1f;

		private static Vector3 _lastScanPos = Vector3.negativeInfinity;

		private static long _lastPlayerID = -1L;

		private static readonly List<Container> _cachedContainers = new List<Container>();

		public static List<Container> GetNearbyContainers(Vector3 position, float radius, long playerID)
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			//IL_0211: Unknown result type (might be due to invalid IL or missing references)
			//IL_0217: Invalid comparison between Unknown and I4
			if (Time.time - _lastScanTime < 0.25f && Vector3.Distance(_lastScanPos, position) < 0.5f && _lastPlayerID == playerID)
			{
				return _cachedContainers;
			}
			_lastScanTime = Time.time;
			_lastScanPos = position;
			_lastPlayerID = playerID;
			_cachedContainers.Clear();
			HashSet<Container> hashSet = new HashSet<Container>();
			try
			{
				_tempPieces.Clear();
				Piece.GetAllPiecesInRadius(position, radius, _tempPieces);
				foreach (Piece tempPiece in _tempPieces)
				{
					if (!((Object)(object)tempPiece == (Object)null))
					{
						Container val = ((Component)tempPiece).GetComponent<Container>() ?? ((Component)tempPiece).GetComponentInChildren<Container>() ?? ((Component)tempPiece).GetComponentInParent<Container>();
						if ((Object)(object)val != (Object)null && (Object)(object)val.m_nview != (Object)null && val.m_nview.IsValid())
						{
							hashSet.Add(val);
						}
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Piece search exception: " + ex.Message));
			}
			try
			{
				int num = LayerMask.GetMask(new string[6] { "piece", "piece_nonsolid", "item", "default", "vehicle", "character_net" });
				if (num == 0)
				{
					num = -1;
				}
				Collider[] array = Physics.OverlapSphere(position, radius, num);
				foreach (Collider val2 in array)
				{
					if (!((Object)(object)val2 == (Object)null))
					{
						Container val3 = ((Component)val2).GetComponentInParent<Container>() ?? ((Component)val2).GetComponent<Container>() ?? ((Component)val2).GetComponentInChildren<Container>();
						if ((Object)(object)val3 != (Object)null && (Object)(object)val3.m_nview != (Object)null && val3.m_nview.IsValid())
						{
							hashSet.Add(val3);
						}
					}
				}
			}
			catch (Exception ex2)
			{
				Plugin.Log.LogWarning((object)("Physics search exception: " + ex2.Message));
			}
			foreach (Container item in hashSet)
			{
				try
				{
					if (!item.IsInUse() && ((int)item.m_privacy == 2 || playerID == 0L || item.CheckAccess(playerID)))
					{
						item.Load();
						item.CheckForChanges();
						if (item.GetInventory() != null)
						{
							_cachedContainers.Add(item);
						}
					}
				}
				catch (Exception ex3)
				{
					Plugin.Log.LogWarning((object)("Container load exception: " + ex3.Message));
				}
			}
			return _cachedContainers;
		}

		public static int CountItemsInContainers(List<Container> containers, string itemName)
		{
			int num = 0;
			if (containers == null)
			{
				return 0;
			}
			foreach (Container container in containers)
			{
				Inventory inventory = container.GetInventory();
				if (inventory != null)
				{
					num += inventory.CountItems(itemName, -1, true);
				}
			}
			return num;
		}

		public static int RemoveItemsFromContainers(List<Container> containers, string itemName, int amount)
		{
			if (containers == null || amount <= 0)
			{
				return 0;
			}
			int num = amount;
			foreach (Container container in containers)
			{
				if (num <= 0)
				{
					break;
				}
				Inventory inventory = container.GetInventory();
				if (inventory != null)
				{
					int num2 = inventory.CountItems(itemName, -1, true);
					if (num2 > 0)
					{
						int num3 = Mathf.Min(num, num2);
						inventory.RemoveItem(itemName, num3, -1, true);
						container.Save();
						num -= num3;
						Plugin.Log.LogInfo((object)$"[NearbyChests] Pulled {num3}x {itemName} from container '{container.m_name}' ({num} more needed)");
					}
				}
			}
			return amount - num;
		}

		public static ItemData FindCookableItemInContainers(List<Container> containers, Smelter smelter)
		{
			if (containers == null || (Object)(object)smelter == (Object)null || smelter.m_conversion == null)
			{
				return null;
			}
			foreach (ItemConversion item2 in smelter.m_conversion)
			{
				if ((Object)(object)item2.m_from == (Object)null)
				{
					continue;
				}
				string name = item2.m_from.m_itemData.m_shared.m_name;
				foreach (Container container in containers)
				{
					Inventory inventory = container.GetInventory();
					if (inventory != null)
					{
						ItemData item = inventory.GetItem(name, -1, false);
						if (item != null)
						{
							Plugin.Log.LogInfo((object)("[NearbyChests] Found cookable ore '" + name + "' in container '" + container.m_name + "'"));
							return item;
						}
					}
				}
			}
			return null;
		}

		public static ItemData FindCookableItemInContainers(List<Container> containers, CookingStation station)
		{
			if (containers == null || (Object)(object)station == (Object)null || station.m_conversion == null)
			{
				return null;
			}
			foreach (ItemConversion item2 in station.m_conversion)
			{
				if ((Object)(object)item2.m_from == (Object)null)
				{
					continue;
				}
				string name = item2.m_from.m_itemData.m_shared.m_name;
				foreach (Container container in containers)
				{
					Inventory inventory = container.GetInventory();
					if (inventory != null)
					{
						ItemData item = inventory.GetItem(name, -1, false);
						if (item != null)
						{
							Plugin.Log.LogInfo((object)("[NearbyChests] Found cookable food '" + name + "' in container '" + container.m_name + "'"));
							return item;
						}
					}
				}
			}
			return null;
		}
	}
	public static class CraftingQueue
	{
		private class Entry
		{
			public Recipe Recipe;

			public ItemData UpgradeItem;

			public int Variant;

			public bool Multi;
		}

		private static readonly Queue<Entry> _queue = new Queue<Entry>();

		private static CraftingStation _station;

		public static int Count => _queue.Count;

		public static void Enqueue(InventoryGui gui, Player player)
		{
			if ((Object)(object)gui == (Object)null || (Object)(object)player == (Object)null)
			{
				return;
			}
			Recipe recipe = ((RecipeDataPair)(ref gui.m_selectedRecipe)).Recipe;
			if (!((Object)(object)recipe == (Object)null))
			{
				CraftingStation currentCraftingStation = player.GetCurrentCraftingStation();
				if ((Object)(object)_station != (Object)(object)currentCraftingStation)
				{
					_queue.Clear();
					_station = currentCraftingStation;
				}
				ItemData itemData = ((RecipeDataPair)(ref gui.m_selectedRecipe)).ItemData;
				_queue.Enqueue(new Entry
				{
					Recipe = recipe,
					UpgradeItem = itemData,
					Variant = gui.m_selectedVariant,
					Multi = (itemData == null && (ZInput.GetButton("AltPlace") || ZInput.GetButton("JoyLStick")))
				});
				string text = Localization.instance.Localize(recipe.m_item.m_itemData.m_shared.m_name);
				((Character)player).Message((MessageType)1, ModLocalization.Get("qol_queue_added", text, _queue.Count.ToString()), 0, (Sprite)null, false);
			}
		}

		public static void Clear(Player player, bool announce)
		{
			if (_queue.Count != 0)
			{
				_queue.Clear();
				if (announce && (Object)(object)player != (Object)null)
				{
					((Character)player).Message((MessageType)1, ModLocalization.Get("qol_queue_cleared"), 0, (Sprite)null, false);
				}
			}
		}

		public static void TryStartNext(InventoryGui gui, Player player)
		{
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			if (_queue.Count == 0 || gui.m_craftTimer >= 0f || (Object)(object)player == (Object)null)
			{
				return;
			}
			CraftingStation currentCraftingStation = player.GetCurrentCraftingStation();
			if ((Object)(object)currentCraftingStation != (Object)(object)_station)
			{
				Clear(player, announce: true);
				return;
			}
			Entry entry = _queue.Dequeue();
			string text = Validate(gui, player, currentCraftingStation, entry);
			if (text != null)
			{
				string text2 = Localization.instance.Localize(entry.Recipe.m_item.m_itemData.m_shared.m_name);
				((Character)player).Message((MessageType)2, ModLocalization.Get("qol_queue_skipped", text2, Localization.instance.Localize(text)), 0, (Sprite)null, false);
				return;
			}
			gui.m_craftRecipe = entry.Recipe;
			gui.m_craftUpgradeItem = entry.UpgradeItem;
			gui.m_craftVariant = entry.Variant;
			gui.m_multiCrafting = entry.Multi;
			gui.m_craftTimer = 0f;
			if ((Object)(object)entry.Recipe.m_craftingStation != (Object)null)
			{
				if ((Object)(object)currentCraftingStation != (Object)null)
				{
					currentCraftingStation.m_craftItemEffects.Create(((Component)player).transform.position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID));
				}
			}
			else
			{
				gui.m_craftItemEffects.Create(((Component)player).transform.position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID));
			}
		}

		private static string Validate(InventoryGui gui, Player player, CraftingStation station, Entry entry)
		{
			Inventory inventory = ((Humanoid)player).GetInventory();
			int num = ((entry.UpgradeItem == null) ? 1 : (entry.UpgradeItem.m_quality + 1));
			int num2 = ((!entry.Multi) ? 1 : gui.m_multiCraftAmount);
			if (entry.UpgradeItem != null && !inventory.ContainsItem(entry.UpgradeItem))
			{
				return "$qol_queue_itemgone";
			}
			if (num > entry.Recipe.m_item.m_itemData.m_shared.m_maxQuality)
			{
				return "$inventory_maxquality";
			}
			if (!player.NoCostCheat() && !ZoneSystem.instance.GetGlobalKey((GlobalKeys)25) && !player.HaveRequirements(entry.Recipe, false, num, num2))
			{
				return "$msg_missingrequirement";
			}
			if ((Object)(object)entry.Recipe.GetRequiredStation(num) != (Object)null && ((Object)(object)station == (Object)null || !station.CheckUsable(player, false)))
			{
				return "$msg_missingstation";
			}
			int num3 = default(int);
			ItemData val = default(ItemData);
			int amount = entry.Recipe.GetAmount(num, ref num3, ref val, num2);
			if (entry.UpgradeItem == null && !inventory.CanAddItem(((Component)entry.Recipe.m_item).gameObject, amount))
			{
				return "$inventory_full";
			}
			return null;
		}
	}
	public static class EquipmentSlots
	{
		public static readonly ItemType[] SlotTypes;

		public static readonly string[] SlotLabelKeys;

		private static Player _player;

		private static int _baseRows;

		public static bool Active
		{
			get
			{
				if ((Object)(object)_player != (Object)null && (Object)(object)_player == (Object)(object)Player.m_localPlayer && Plugin.ModEnabled.Value)
				{
					return Plugin.EquipmentSlots.Value;
				}
				return false;
			}
		}

		public static int SlotRow => _baseRows;

		public static int BaseRows => _baseRows;

		public static bool IsSlotInventory(Inventory inventory)
		{
			if (Active)
			{
				return inventory == ((Humanoid)_player).m_inventory;
			}
			return false;
		}

		public static bool IsSlotCell(Inventory inventory, int y)
		{
			if (IsSlotInventory(inventory))
			{
				return y == _baseRows;
			}
			return false;
		}

		public static int NormalRows(Inventory inventory)
		{
			if (!IsSlotInventory(inventory))
			{
				return inventory.GetHeight();
			}
			return _baseRows;
		}

		public static int SlotIndex(ItemData item)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			if (item != null)
			{
				return Array.IndexOf(SlotTypes, item.m_shared.m_itemType);
			}
			return -1;
		}

		public static bool Fits(ItemData item, int x)
		{
			if (x >= 0 && x < SlotTypes.Length)
			{
				return SlotIndex(item) == x;
			}
			return false;
		}

		public static void SetBaseRows(Player player, int rows, int fromRow)
		{
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			Inventory inventory = ((Humanoid)player).m_inventory;
			if (fromRow != rows)
			{
				foreach (ItemData allItem in inventory.GetAllItems())
				{
					if (allItem.m_gridPos.y == fromRow && allItem.m_gridPos.x < SlotTypes.Length)
					{
						allItem.m_gridPos = new Vector2i(allItem.m_gridPos.x, rows);
					}
				}
				Plugin.Log.LogInfo((object)$"[EquipmentSlots] Slot row moved from row {fromRow + 1} to row {rows + 1}.");
			}
			_player = player;
			_baseRows = rows;
			inventory.SetHeight(rows + 1);
		}

		public static bool IsReadyFor(Player player)
		{
			if ((Object)(object)_player == (Object)(object)player)
			{
				return (Object)(object)player != (Object)null;
			}
			return false;
		}

		public static void Forget()
		{
			_player = null;
		}

		public static Vector2i FindFreeNormalCell(Inventory inventory, bool topFirst)
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			int num = NormalRows(inventory);
			int width = inventory.GetWidth();
			for (int i = 0; i < num; i++)
			{
				int num2 = (topFirst ? i : (num - 1 - i));
				for (int j = 0; j < width; j++)
				{
					if (inventory.GetItemAt(j, num2) == null)
					{
						return new Vector2i(j, num2);
					}
				}
			}
			return new Vector2i(-1, -1);
		}

		public static int FreeNormalCells(Inventory inventory)
		{
			int num = NormalRows(inventory);
			int num2 = 0;
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (allItem.m_gridPos.y < num)
				{
					num2++;
				}
			}
			return inventory.GetWidth() * num - num2;
		}

		public static void Sweep(Player player)
		{
			if (!Active || (Object)(object)player != (Object)(object)_player)
			{
				return;
			}
			bool flag = false;
			ItemData[] array = ((Humanoid)player).m_inventory.GetAllItems().ToArray();
			foreach (ItemData val in array)
			{
				int num = SlotIndex(val);
				if (num >= 0 && val.m_equipped && (val.m_gridPos.y != _baseRows || val.m_gridPos.x != num))
				{
					flag |= MoveIntoSlot(((Humanoid)player).m_inventory, val);
				}
			}
			if (flag)
			{
				((Humanoid)player).m_inventory.Changed(false, false);
			}
		}

		public static bool MoveIntoSlot(Inventory inventory, ItemData item)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			int num = SlotIndex(item);
			if (num < 0)
			{
				return false;
			}
			Vector2i val = default(Vector2i);
			((Vector2i)(ref val))..ctor(num, _baseRows);
			if (item.m_gridPos == val)
			{
				return false;
			}
			ItemData itemAt = inventory.GetItemAt(val.x, val.y);
			if (itemAt != null)
			{
				itemAt.m_gridPos = item.m_gridPos;
			}
			item.m_gridPos = val;
			return true;
		}

		public static bool MoveOutOfSlot(Inventory inventory, ItemData item)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			Vector2i val = FindFreeNormalCell(inventory, topFirst: false);
			if (val.x < 0)
			{
				return false;
			}
			item.m_gridPos = val;
			return true;
		}

		public static bool Remove(Player player)
		{
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)_player)
			{
				return true;
			}
			Inventory inventory = ((Humanoid)player).m_inventory;
			List<ItemData> list = new List<ItemData>();
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (allItem.m_gridPos.y == _baseRows)
				{
					list.Add(allItem);
				}
			}
			if (list.Count > FreeCells(inventory, _baseRows))
			{
				return false;
			}
			foreach (ItemData item in list)
			{
				item.m_gridPos = FindFreeCell(inventory, _baseRows);
			}
			inventory.SetHeight(_baseRows);
			if ((Object)(object)InventoryGui.instance != (Object)null)
			{
				InventoryGui.instance.SetInventorySize(_baseRows);
			}
			inventory.Changed(false, false);
			_player = null;
			Plugin.Log.LogInfo((object)"[EquipmentSlots] Slot row removed; worn items moved back into the inventory.");
			return true;
		}

		public static bool RescueStranded(Player player, int rows)
		{
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			Inventory inventory = ((Humanoid)player).m_inventory;
			List<ItemData> list = new List<ItemData>();
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (allItem.m_gridPos.y >= rows)
				{
					list.Add(allItem);
				}
			}
			if (list.Count == 0)
			{
				return true;
			}
			bool flag = true;
			foreach (ItemData item in list)
			{
				Vector2i val = FindFreeCell(inventory, rows);
				if (val.x < 0)
				{
					flag = false;
					break;
				}
				item.m_gridPos = val;
			}
			Plugin.Log.LogInfo((object)($"[EquipmentSlots] {list.Count} item(s) from the equipment slots " + (flag ? "moved into the inventory." : "kept in an extra row (no room).")));
			return flag;
		}

		private static Vector2i FindFreeCell(Inventory inventory, int rows)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			for (int num = rows - 1; num >= 0; num--)
			{
				for (int i = 0; i < inventory.GetWidth(); i++)
				{
					if (inventory.GetItemAt(i, num) == null)
					{
						return new Vector2i(i, num);
					}
				}
			}
			return new Vector2i(-1, -1);
		}

		private static int FreeCells(Inventory inventory, int rows)
		{
			int num = 0;
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (allItem.m_gridPos.y < rows)
				{
					num++;
				}
			}
			return inventory.GetWidth() * rows - num;
		}

		static EquipmentSlots()
		{
			ItemType[] array = new ItemType[6];
			RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
			SlotTypes = (ItemType[])(object)array;
			SlotLabelKeys = new string[6] { "qol_slot_helmet", "qol_slot_chest", "qol_slot_legs", "qol_slot_cape", "qol_slot_utility", "qol_slot_trinket" };
			_baseRows = 4;
		}
	}
	public static class InventorySorter
	{
		public const int HotbarRows = 1;

		public static void Sort(Inventory inventory, int firstRow, int endRow)
		{
			//IL_01ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d3: Unknown result type (might be due to invalid IL or missing references)
			int width = inventory.GetWidth();
			List<ItemData> list = new List<ItemData>();
			foreach (ItemData item in inventory.m_inventory)
			{
				if (item.m_gridPos.y >= firstRow && item.m_gridPos.y < endRow)
				{
					list.Add(item);
				}
			}
			if (list.Count == 0)
			{
				return;
			}
			list.Sort((ItemData a, ItemData b) => b.m_equipped.CompareTo(a.m_equipped));
			List<ItemData> list2 = new List<ItemData>();
			foreach (ItemData item2 in list)
			{
				if (!item2.m_equipped && item2.m_shared.m_maxStackSize > 1)
				{
					foreach (ItemData item3 in list2)
					{
						int num = item3.m_shared.m_maxStackSize - item3.m_stack;
						if (num > 0 && item3.IsSameType(item2))
						{
							int num2 = Mathf.Min(num, item2.m_stack);
							item3.m_stack += num2;
							item2.m_stack -= num2;
							if (item2.m_stack <= 0)
							{
								break;
							}
						}
					}
					if (item2.m_stack <= 0)
					{
						inventory.m_inventory.Remove(item2);
						continue;
					}
				}
				list2.Add(item2);
			}
			list2.Sort(Compare);
			for (int num3 = 0; num3 < list2.Count; num3++)
			{
				list2[num3].m_gridPos = new Vector2i(num3 % width, firstRow + num3 / width);
			}
			inventory.Changed(false, false);
		}

		public static bool Restock(Player player, IList<Container> chests)
		{
			Inventory inventory = ((Humanoid)player).GetInventory();
			Dictionary<string, int> dictionary = new Dictionary<string, int>();
			Dictionary<string, Sprite> dictionary2 = new Dictionary<string, Sprite>();
			HashSet<Container> hashSet = new HashSet<Container>();
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (!IsRestockable(allItem))
				{
					continue;
				}
				int num = allItem.m_shared.m_maxStackSize - allItem.m_stack;
				if (num <= 0)
				{
					continue;
				}
				foreach (Container chest in chests)
				{
					if (num <= 0)
					{
						break;
					}
					Inventory inventory2 = chest.GetInventory();
					if (inventory2 == null)
					{
						continue;
					}
					foreach (ItemData item in new List<ItemData>(inventory2.GetAllItems()))
					{
						if (item.IsSameType(allItem))
						{
							int num2 = Mathf.Min(num, item.m_stack);
							inventory2.RemoveItem(item, num2);
							allItem.m_stack += num2;
							num -= num2;
							hashSet.Add(chest);
							string name = allItem.m_shared.m_name;
							dictionary.TryGetValue(name, out var value);
							dictionary[name] = value + num2;
							dictionary2[name] = allItem.GetIcon();
							if (num <= 0)
							{
								break;
							}
						}
					}
				}
			}
			if (dictionary.Count == 0)
			{
				return false;
			}
			foreach (Container item2 in hashSet)
			{
				item2.Save();
			}
			inventory.Changed(false, false);
			foreach (KeyValuePair<string, int> item3 in dictionary)
			{
				((Character)player).Message((MessageType)1, ModLocalization.Get("qol_restock_taken", Localization.instance.Localize(item3.Key)), item3.Value, dictionary2[item3.Key], false);
			}
			return true;
		}

		private static bool IsRestockable(ItemData item)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Invalid comparison between Unknown and I4
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Invalid comparison between Unknown and I4
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Invalid comparison between Unknown and I4
			if (item.m_shared.m_maxStackSize <= 1)
			{
				return false;
			}
			ItemType itemType = item.m_shared.m_itemType;
			if ((int)itemType == 2 || (int)itemType == 9 || (int)itemType == 23)
			{
				return true;
			}
			return false;
		}

		private static int Compare(ItemData a, ItemData b)
		{
			int num = KindOrder(a).CompareTo(KindOrder(b));
			if (num != 0)
			{
				return num;
			}
			int num2 = string.Compare(Localization.instance.Localize(a.m_shared.m_name), Localization.instance.Localize(b.m_shared.m_name), StringComparison.CurrentCultureIgnoreCase);
			if (num2 != 0)
			{
				return num2;
			}
			int num3 = b.m_quality.CompareTo(a.m_quality);
			if (num3 != 0)
			{
				return num3;
			}
			return b.m_stack.CompareTo(a.m_stack);
		}

		private static int KindOrder(ItemData item)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Expected I4, but got Unknown
			ItemType itemType = item.m_shared.m_itemType;
			switch (itemType - 1)
			{
			case 2:
			case 3:
			case 13:
			case 19:
			case 21:
				return 0;
			case 4:
				return 1;
			case 18:
				return 2;
			case 14:
				return 3;
			case 8:
			case 22:
				return 4;
			case 5:
			case 6:
			case 10:
			case 11:
			case 16:
				return 5;
			case 17:
				return 6;
			case 23:
				return 7;
			case 1:
				return 8;
			case 20:
				return 9;
			case 0:
				return 10;
			case 12:
				return 11;
			default:
				return 12;
			}
		}
	}
	public static class ModAssets
	{
		private static readonly Dictionary<string, Sprite> Sprites = new Dictionary<string, Sprite>();

		private static readonly MethodInfo LoadImage = Type.GetType("UnityEngine.ImageConversion, UnityEngine.ImageConversionModule")?.GetMethod("LoadImage", new Type[2]
		{
			typeof(Texture2D),
			typeof(byte[])
		});

		public static Sprite StatsIcon => LoadSprite("StatsIcon.png");

		public static Sprite TrashIcon => LoadSprite("TrashIcon.png");

		public static Sprite LoadSprite(string fileName)
		{
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Expected O, but got Unknown
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			if (Sprites.TryGetValue(fileName, out var value))
			{
				return value;
			}
			Sprite val = null;
			using (Stream stream = typeof(ModAssets).Assembly.GetManifestResourceStream("ValheimPureQoLModPack." + fileName))
			{
				if (stream == null)
				{
					Plugin.Log.LogWarning((object)("[Assets] " + fileName + " is not embedded in the DLL."));
				}
				else
				{
					MemoryStream memoryStream = new MemoryStream();
					stream.CopyTo(memoryStream);
					Texture2D val2 = new Texture2D(2, 2, (TextureFormat)4, true);
					if (LoadImage != null && (bool)LoadImage.Invoke(null, new object[2]
					{
						val2,
						memoryStream.ToArray()
					}))
					{
						((Object)val2).name = fileName;
						((Texture)val2).wrapMode = (TextureWrapMode)1;
						val = Sprite.Create(val2, new Rect(0f, 0f, (float)((Texture)val2).width, (float)((Texture)val2).height), new Vector2(0.5f, 0.5f));
						((Object)val).name = fileName;
					}
					else
					{
						Plugin.Log.LogWarning((object)("[Assets] " + fileName + " could not be decoded."));
					}
				}
			}
			Sprites[fileName] = val;
			return val;
		}
	}
	public static class ModLocalization
	{
		private static readonly Dictionary<string, string> English = new Dictionary<string, string>
		{
			{ "qol_hint_addall", "Add all" },
			{ "qol_hint_addallfuel", "Add all fuel" },
			{ "qol_hint_addallfood", "Add all food" },
			{ "qol_hint_inclchests", "(incl. nearby chests)" },
			{ "qol_quickstack_button", "Quick Stack" },
			{ "qol_quickstack_nochests", "No nearby chests to quick-stack into." },
			{ "qol_quickstack_nothing", "Nothing to quick-stack." },
			{ "qol_quickstack_stored", "Stored $1" },
			{ "qol_quickstack_noroom", "No room for $1" },
			{ "qol_sort_button", "Sort" },
			{ "qol_button_hotkey", "Hotkey: $1 (with the inventory open)" },
			{ "qol_quickstack_tip", "Puts your items into nearby chests that already hold them. Your hotbar stays as it is." },
			{ "qol_restock_tip", "Fills up the ammo, food and potion stacks you carry from nearby chests." },
			{ "qol_sort_tip", "Sorts your inventory by kind, then by name, merging partial stacks. Your hotbar stays as it is." },
			{ "qol_sortchest_tip", "Sorts this chest by kind, then by name, merging partial stacks." },
			{ "qol_restock_button", "Restock" },
			{ "qol_restock_taken", "Restocked $1" },
			{ "qol_restock_nothing", "Nothing to restock from nearby chests." },
			{ "qol_restock_nochests", "No nearby chests to restock from." },
			{ "qol_trash_label", "Trash" },
			{ "qol_trash_hint", "Drag an item onto the trash to delete it." },
			{ "qol_trash_confirm", "Delete" },
			{ "qol_slot_helmet", "Head" },
			{ "qol_slot_chest", "Chest" },
			{ "qol_slot_legs", "Legs" },
			{ "qol_slot_cape", "Cape" },
			{ "qol_slot_utility", "Utility" },
			{ "qol_slot_trinket", "Trinket" },
			{ "qol_slot_only", "Only a $1 item goes in this slot." },
			{ "qol_slot_unused", "This cell isn't used." },
			{ "qol_slot_noroom", "No room in your inventory; it stays in its slot." },
			{ "qol_slot_noroom_off", "Not enough room for your worn gear: make room in your inventory first. The equipment slots stay on." },
			{ "qol_slot_off_done", "Equipment slots off: your worn gear is back in your inventory." },
			{ "qol_slot_kept", "Your gear from the equipment slots is kept in an extra row until you make room." },
			{ "qol_queue_button", "Queue" },
			{ "qol_queue_button_count", "Queue ($1)" },
			{ "qol_queue_added", "Queued $1 ($2 in queue)" },
			{ "qol_queue_cleared", "Crafting queue cleared" },
			{ "qol_queue_skipped", "$1: $2 (skipped)" },
			{ "qol_queue_itemgone", "item no longer in inventory" },
			{ "qol_sticky_attack_on", "Attack locked" },
			{ "qol_sticky_attack_off", "Attack released" },
			{ "qol_sticky_block_on", "Block locked" },
			{ "qol_sticky_block_off", "Block released" },
			{ "qol_remove_emptycargo", "Empty its cargo first" },
			{ "qol_repair_area", "Repaired $1 pieces" },
			{ "qol_repair_none", "Nothing nearby needs repair" },
			{ "qol_undo_nothing", "Nothing to undo" },
			{ "qol_undo_placed", "Undid placing $1" },
			{ "qol_undo_removed", "Put $1 back" },
			{ "qol_undo_gone", "$1 is already gone" },
			{ "qol_undo_nomaterials", "Not enough materials to put $1 back" },
			{ "qol_autoeat_ate", "Ate $1 again" },
			{ "qol_stats_button", "Stats" },
			{ "qol_stats_title", "Player Stats" },
			{ "qol_stat_kills", "Enemies killed" },
			{ "qol_stat_kills_desc", "Enemies and bosses you have killed since you last died (counted since this mod was installed). Click to see each enemy type." },
			{ "qol_stat_skillups", "Skill levels gained" },
			{ "qol_stat_skillups_desc", "Skill levels you have gained since you last died (counted since this mod was installed). Click to see each skill." },
			{ "qol_stat_none", "None yet" },
			{ "qol_stat_days", "Days alive (in-game)" },
			{ "qol_stat_days_desc", "Full in-game days you have survived since you last died." },
			{ "qol_stat_realtime", "Time alive (real time)" },
			{ "qol_stat_realtime_desc", "Real time played since you last died (counted since this mod was installed)." },
			{ "qol_stats_section_life", "Since last death" },
			{ "qol_stats_section_overall", "Overall" },
			{ "qol_stats_section_prevlife", "Previous life" },
			{ "qol_stats_prevlife_empty", "Recorded from your next death on." },
			{ "qol_stat_prev_kills_desc", "Enemies and bosses you killed during your previous life. Click to see each enemy type." },
			{ "qol_stat_prev_skillups_desc", "Skill levels you gained during your previous life. Click to see each skill." },
			{ "qol_stat_prev_days_desc", "Full in-game days your previous life lasted." },
			{ "qol_stat_prev_realtime_desc", "Real time your previous life lasted." },
			{ "qol_stats_section_skillloss", "Skills lost last death" },
			{ "qol_stat_skillloss", "Skill levels lost" },
			{ "qol_stat_skillloss_desc", "Skill levels taken by your last death that cost any, in total and per skill. Deaths shortly after another death cost no skills and are skipped here." },
			{ "qol_stat_skillloss_none", "No skill levels were lost" },
			{ "qol_stat_kills_total_desc", "Every enemy and boss this character has killed, as recorded by the game. Click to see each enemy type." },
			{ "qol_stat_deaths", "Deaths" },
			{ "qol_stat_deaths_desc", "Times this character has died." },
			{ "qol_stat_longestlife", "Longest life (in-game days)" },
			{ "qol_stat_longestlife_desc", "Most full in-game days survived without dying." },
			{ "qol_stat_playtime", "Time played (real time)" },
			{ "qol_stat_playtime_desc", "Total real time played with this character, as recorded by the game." },
			{ "qol_time_dhm", "$1d $2h $3m" },
			{ "qol_time_hm", "$1h $2m" },
			{ "qol_time_ms", "$1m $2s" },
			{ "qol_net_ping", "$1 ms" },
			{ "qol_net_serverdelay", "server +$1 ms" },
			{ "qol_net_loss", "$1% loss" },
			{ "qol_net_nearby", "$1 nearby" },
			{ "qol_net_owner", "Enemies: $1" },
			{ "qol_net_owner_you", "you" },
			{ "qol_net_owner_other", "another player" },
			{ "qol_settings_tab", "QoL" },
			{ "qol_cfg_general", "General" },
			{ "qol_cfg_general_enabled", "Mod enabled" },
			{ "qol_cfg_general_enabled_desc", "Turns every feature of the QoL mod pack on or off. After switching it back on, restart the game; this tab stays available either way." },
			{ "qol_cfg_general_zoomoutonfoot", "Zoom out on foot" },
			{ "qol_cfg_general_zoomoutonfoot_desc", "How far the camera can zoom out on foot, as a multiple of the game's limit; 1x is the normal game." },
			{ "qol_cfg_general_zoomoutmounted", "Zoom out while riding" },
			{ "qol_cfg_general_zoomoutmounted_desc", "How far the camera can zoom out while riding a mount, as a multiple of the game's on-foot limit (the game uses that one for riding too); 1x is the normal game." },
			{ "qol_cfg_general_zoomoutboat", "Zoom out while steering a ship" },
			{ "qol_cfg_general_zoomoutboat_desc", "How far the camera can zoom out while steering a ship, as a multiple of the game's steering limit (already farther than on foot); 1x is the normal game." },
			{ "qol_cfg_general_equipmentinwater", "Equipment in water" },
			{ "qol_cfg_general_equipmentinwater_desc", "While swimming, weapons, tools, shields and torches stay in your hands, and you can equip and use them. Swimming still drains stamina as usual." },
			{ "qol_cfg_chestsstations", "Chests & Stations" },
			{ "qol_cfg_chestsstations_batchdepositenabled", "Fill stations in one go" },
			{ "qol_cfg_chestsstations_batchdepositenabled_desc", "Hold the modifier key (Left Shift by default) while pressing E to fill a smelter, kiln, fire or cooking station with everything it takes." },
			{ "qol_cfg_chestsstations_showtooltiphint", "Show the fill-in-one-go key hint" },
			{ "qol_cfg_chestsstations_showtooltiphint_desc", "Show the batch deposit key in a station's hover text." },
			{ "qol_cfg_chestsstations_autopullfromchests", "Use items from nearby chests" },
			{ "qol_cfg_chestsstations_autopullfromchests_desc", "Fuel and building or crafting materials can come from chests near the station or build spot, not only from your inventory." },
			{ "qol_cfg_chestsstations_quickstackenabled", "Quick Stack button" },
			{ "qol_cfg_chestsstations_quickstackenabled_desc", "Adds a Quick Stack button to the inventory that sends your items into nearby chests." },
			{ "qol_cfg_chestsstations_quickstackonlyexistingstacks", "Only chests that already hold the item" },
			{ "qol_cfg_chestsstations_sortenabled", "Sort buttons" },
			{ "qol_cfg_chestsstations_sortenabled_desc", "Sort buttons for your inventory (under Restock) and for the open chest. Partial stacks are merged and items are laid out by kind, then by name. Your hotbar row stays as it is." },
			{ "qol_cfg_chestsstations_restockenabled", "Restock button" },
			{ "qol_cfg_chestsstations_restockenabled_desc", "A Restock button in the inventory that fills up the partial ammo, food and potion stacks you carry from nearby chests, the open one included." },
			{ "qol_cfg_chestsstations_restockrange", "Restock range" },
			{ "qol_cfg_chestsstations_restockrange_desc", "How far around you chests are used by Restock." },
			{ "qol_cfg_chestsstations_quickstackkey", "Quick Stack key" },
			{ "qol_cfg_chestsstations_quickstackkey_desc", "Key that quick-stacks into nearby chests while the inventory is open. None turns it off." },
			{ "qol_cfg_chestsstations_restockkey", "Restock key" },
			{ "qol_cfg_chestsstations_restockkey_desc", "Key that restocks from nearby chests while the inventory is open. None turns it off." },
			{ "qol_cfg_chestsstations_sortkey", "Sort key" },
			{ "qol_cfg_chestsstations_sortkey_desc", "Key that sorts while the inventory is open: the open chest, or your inventory when no chest is open. None turns it off." },
			{ "qol_cfg_chestsstations_trashenabled", "Trash slot" },
			{ "qol_cfg_chestsstations_trashenabled_desc", "A trash slot under your armor value: drop an item on it and confirm to delete it for good. Hold Shift to skip the confirmation." },
			{ "qol_cfg_chestsstations_equipmentslots", "Equipment slots" },
			{ "qol_cfg_chestsstations_equipmentslots_desc", "Your worn helmet, chest, legs, cape, utility item and trinket sit in their own panel beside the inventory, so they don't take inventory space. Turning this off moves them back into your inventory, still worn: it needs free room for them, and without it this stays on. Nothing is deleted. Before removing the mod, turn this off first, or the game drops your worn gear at your feet the next time you load." },
			{ "qol_cfg_chestsstations_quickstackonlyexistingstacks_desc", "Quick Stack only puts an item into chests that already contain it, instead of any chest with room." },
			{ "qol_cfg_crafting_repairall", "Repair everything at once" },
			{ "qol_cfg_crafting_repairall_desc", "The Repair button at a crafting station repairs every item it can, instead of one at a time." },
			{ "qol_cfg_crafting", "Crafting" },
			{ "qol_cfg_crafting_queueenabled", "Queue button" },
			{ "qol_cfg_crafting_queueenabled_desc", "Adds a Queue button next to Craft, to line up several crafts or upgrades that run one after another." },
			{ "qol_cfg_crafting_searchenabled", "Recipe search" },
			{ "qol_cfg_crafting_searchenabled_desc", "Adds a search field above the recipe list of every crafting interface (hand crafting, workbench, forge, cauldron and the rest). Only recipes whose name contains the text are listed, in both Craft and Upgrade." },
			{ "qol_cfg_combatsurvival", "Combat & Survival" },
			{ "qol_cfg_combatsurvival_stickyattack", "Sticky attack" },
			{ "qol_cfg_combatsurvival_stickyattack_desc", "Hold the modifier key (Left Alt by default) while clicking Attack to keep attacking until you click Attack again. Bows stay drawn." },
			{ "qol_cfg_combatsurvival_stickyblock", "Sticky block" },
			{ "qol_cfg_combatsurvival_stickyblock_desc", "Hold the modifier key while clicking Block to keep blocking until you click Block again. Not used while the game's own toggle block setting is on." },
			{ "qol_cfg_interface", "Interface" },
			{ "qol_cfg_interface_playerstatsenabled", "Stats button" },
			{ "qol_cfg_interface_playerstatsenabled_desc", "Adds a Stats button next to Skills in the inventory: kills, skill levels and time alive since your last death, and more. Counting pauses while this is off." },
			{ "qol_cfg_interface_netinfoenabled", "Network info under the minimap" },
			{ "qol_cfg_interface_netinfoenabled_desc", "A small line under the minimap that tells whether lag comes from your connection, the server or another player. Only shown when playing online." },
			{ "qol_cfg_interface_showping", "Ping" },
			{ "qol_cfg_interface_showping_desc", "Your ping to the server; when hosting, the average ping of the players connected to you. Yellow and red when high." },
			{ "qol_cfg_interface_showserverdelay", "Server delay" },
			{ "qol_cfg_interface_showserverdelay_desc", "Shown when the server takes clearly longer to answer than the network explains, meaning the server itself is overloaded. Not shown while hosting." },
			{ "qol_cfg_interface_showpacketloss", "Packet loss" },
			{ "qol_cfg_interface_showpacketloss_desc", "Shown when packets get lost on your connection, which causes rubber-banding even with a low ping." },
			{ "qol_cfg_interface_shownearbyplayers", "Players nearby" },
			{ "qol_cfg_interface_shownearbyplayers_desc", "How many other players are close enough to share your area." },
			{ "qol_cfg_interface_showenemyowner", "Who runs the enemies" },
			{ "qol_cfg_interface_showenemyowner_desc", "With players nearby, shows whose game runs most creatures around you. When it's someone else, what you feel from them is about your ping plus theirs." },
			{ "qol_cfg_chestsstations_batchdepositmodifierkey", "Fill-in-one-go key" },
			{ "qol_cfg_chestsstations_batchdepositmodifierkey_desc", "Key to hold while pressing E to fill a station in one go." },
			{ "qol_cfg_chestsstations_autopullrange", "Nearby chests range" },
			{ "qol_cfg_chestsstations_autopullrange_desc", "How far from the station or build spot chests are used." },
			{ "qol_cfg_chestsstations_quickstackrange", "Quick Stack range" },
			{ "qol_cfg_chestsstations_quickstackrange_desc", "How far around you chests are used by Quick Stack." },
			{ "qol_cfg_crafting_queuebuttonoffsetx", "Queue button position (sideways)" },
			{ "qol_cfg_crafting_queuebuttonoffsetx_desc", "Moves the Queue button left or right from its spot next to Craft." },
			{ "qol_cfg_crafting_queuebuttonoffsety", "Queue button position (height)" },
			{ "qol_cfg_crafting_queuebuttonoffsety_desc", "Moves the Queue button down or up from its spot next to Craft." },
			{ "qol_cfg_combatsurvival_stickymodifierkey", "Sticky attack/block key" },
			{ "qol_cfg_combatsurvival_stickymodifierkey_desc", "Key to hold while clicking Attack or Block to keep it held. Plain clicks stay as in the normal game." },
			{ "qol_cfg_interface_playerstatsbuttonoffsetx", "Stats button position (sideways)" },
			{ "qol_cfg_interface_playerstatsbuttonoffsetx_desc", "Moves the Stats button left or right from its spot after the Skills and Trophies buttons." },
			{ "qol_cfg_interface_playerstatsbuttonoffsety", "Stats button position (height)" },
			{ "qol_cfg_interface_playerstatsbuttonoffsety_desc", "Moves the Stats button down or up from its spot after the Skills and Trophies buttons." },
			{ "qol_cfg_interface_pingwarnms", "Ping yellow from" },
			{ "qol_cfg_interface_pingwarnms_desc", "Ping from which it shows in yellow." },
			{ "qol_cfg_interface_pingbadms", "Ping red from" },
			{ "qol_cfg_interface_pingbadms_desc", "Ping from which it shows in red. It also turns red while the game shows its bad connection warning." },
			{ "qol_cfg_interface_serverdelayminms", "Show server delay from" },
			{ "qol_cfg_interface_serverdelayminms_desc", "Server delay from which it is shown. Red from twice this." },
			{ "qol_cfg_interface_packetlossminpercent", "Show packet loss from" },
			{ "qol_cfg_interface_packetlossminpercent_desc", "Packet loss from which it is shown. Red from 5%." },
			{ "qol_cfg_interface_netinfofontsize", "Network info text size" },
			{ "qol_cfg_interface_netinfofontsize_desc", "Size of the network info text." },
			{ "qol_cfg_interface_netinfooffsetx", "Network info position (sideways)" },
			{ "qol_cfg_interface_netinfooffsetx_desc", "Moves the network info line left or right from its spot under the minimap." },
			{ "qol_cfg_interface_netinfooffsety", "Network info position (height)" },
			{ "qol_cfg_interface_netinfooffsety_desc", "Moves the network info line down or up from its spot under the minimap." },
			{ "qol_cfg_combatsurvival_autoeatenabled", "Eat again before food runs out" },
			{ "qol_cfg_combatsurvival_autoeatenabled_desc", "When one of your foods is about to run out, eats the same food again from your inventory. Nothing happens if you don't carry that food." },
			{ "qol_cfg_combatsurvival_autoeatthresholdpercent", "Eat again when this much is left" },
			{ "qol_cfg_combatsurvival_autoeatthresholdpercent_desc", "How much of a food's time is left when it gets eaten again. The game only lets you eat a food again once it's below half, so 50% is the most." },
			{ "qol_cfg_cheats", "Cheats" },
			{ "qol_cfg_cheats_lowstaminause", "Almost no stamina use" },
			{ "qol_cfg_cheats_lowstaminause_desc", "Running, jumping, swimming, attacking, blocking and dodging use only 0.01% of the normal stamina. Achievements still unlock." },
			{ "qol_cfg_cheats_loweitruse", "Almost no eitr use" },
			{ "qol_cfg_cheats_loweitruse_desc", "Staffs and other eitr users spend only 0.01% of the normal eitr. Achievements still unlock." },
			{ "qol_cfg_cheats_skillgainmultiplier", "Skill experience" },
			{ "qol_cfg_cheats_skillgainmultiplier_desc", "Multiplies the experience every skill gains; 1x is the normal game. Achievements still unlock." },
			{ "qol_cfg_cheats_neveroverweight", "Never overweight" },
			{ "qol_cfg_cheats_neveroverweight_desc", "Carrying more than your weight limit never slows you down or drains stamina. Achievements still unlock." },
			{ "qol_cfg_cheats_dropchancemultiplier", "Drop chance" },
			{ "qol_cfg_cheats_dropchancemultiplier_desc", "Multiplies the chance of every creature drop except trophies, up to always dropping; 1x is the normal game. On a server, only creatures your game runs (see the network info line). Achievements still unlock." },
			{ "qol_cfg_cheats_trophychancemultiplier", "Trophy chance" },
			{ "qol_cfg_cheats_trophychancemultiplier_desc", "Multiplies the chance of creatures dropping their trophy, up to always dropping; 1x is the normal game. On a server, only creatures your game runs (see the network info line). Achievements still unlock." },
			{ "qol_cfg_cheats_spawnratemultiplier", "Spawn rate" },
			{ "qol_cfg_cheats_spawnratemultiplier_desc", "How often creatures spawn in the open world, from nests and spawners, and at fixed spawn points such as crypts; 1.0x is the normal game. How many creatures an area holds is set by Spawn population, and raids keep their normal rate. On a server, only areas your game runs. Achievements still unlock." },
			{ "qol_cfg_cheats_spawnpopulationmultiplier", "Spawn population" },
			{ "qol_cfg_cheats_spawnpopulationmultiplier_desc", "How many of a creature an area of the open world or a nest can hold at once; 1x is the normal game. Fixed spawn points such as crypts always hold one, and raids stay normal. Raise Spawn rate too so the extra creatures fill in quickly. On a server, only areas your game runs. Achievements still unlock." },
			{ "qol_cfg_cheats_spawngroupsizemultiplier", "Pack size" },
			{ "qol_cfg_cheats_spawngroupsizemultiplier_desc", "Multiplies the size of creature packs spawning in the open world: wolves in packs of 1-4 come in 2-8 at 2x; 1x is the normal game. The area's creature limit grows with it so the bigger packs fit. Nests, crypts and raids stay normal. On a server, only areas your game runs. Achievements still unlock." },
			{ "qol_cfg_building", "Building" },
			{ "qol_cfg_building_deconstructtoinventory", "Deconstructed materials to inventory" },
			{ "qol_cfg_building_deconstructtoinventory_desc", "Materials from pieces you remove with the hammer go straight into your inventory instead of falling on the ground, even past your weight limit. Whatever doesn't fit in a free slot still drops." },
			{ "qol_cfg_building_removablecarts", "Remove carts with the hammer" },
			{ "qol_cfg_building_removablecarts_desc", "Carts can be removed with the hammer like other pieces, giving their materials back. Nobody may be pulling the cart and its cargo must be empty." },
			{ "qol_cfg_building_removableships", "Remove ships with the hammer" },
			{ "qol_cfg_building_removableships_desc", "Ships can be removed with the hammer like other pieces, giving their materials back. Nobody may be aboard and the cargo must be empty." },
			{ "qol_cfg_building_arearepair", "Area repair" },
			{ "qol_cfg_building_arearepair_desc", "With the hammer's repair tool selected, hold Attack to repair every damaged piece around you. Each piece costs the same stamina and hammer wear as repairing it by hand. A plain click repairs one piece as usual." },
			{ "qol_cfg_building_arearepairradius", "Area repair range" },
			{ "qol_cfg_building_arearepairradius_desc", "How far around you area repair reaches." },
			{ "qol_cfg_building_buildundo", "Undo placing and removing" },
			{ "qol_cfg_building_buildundo_desc", "While the hammer is out, the undo key reverses your last placements and removals, up to 3. Undoing a placement refunds its materials; undoing a removal puts the piece back and costs its materials again. Sign text, chest contents and the like aren't restored." },
			{ "qol_cfg_building_undokey", "Undo key" },
			{ "qol_cfg_building_undokey_desc", "Key that undoes the last placement or removal, together with the undo modifier key." },
			{ "qol_cfg_building_undomodifierkey", "Undo modifier key" },
			{ "qol_cfg_building_undomodifierkey_desc", "Key to hold together with the undo key. None means the undo key alone. Left Ctrl is also crouch, which is harmless in build mode." },
			{ "qol_cfg_building_seasonalitemsallyear", "Seasonal items all year" },
			{ "qol_cfg_building_seasonalitemsallyear_desc", "Seasonal pieces and recipes (Yule, Halloween, Midsummer...) can be learned, built and crafted all year, with their normal materials and stations, as during their season." },
			{ "qol_cfg_building_stabilitypreview", "Stability preview" },
			{ "qol_cfg_building_stabilitypreview_desc", "While placing a piece, its preview shows the support it would get there, in the colors the hammer shows on placed pieces: blue is full support, green to red is weaker, and red means it would fall." },
			{ "qol_cfg_timeweather", "Time & Weather" },
			{ "qol_cfg_timeweather_timeofday", "Time of day" },
			{ "qol_cfg_timeweather_timeofday_desc", "Normal day and night, always day (noon) or always night (midnight). Only on your game: on a server, others keep the normal sky and sleeping follows the server's clock." },
			{ "qol_cfg_timeweather_timeofday_normal", "Normal" },
			{ "qol_cfg_timeweather_timeofday_alwaysday", "Always day" },
			{ "qol_cfg_timeweather_timeofday_alwaysnight", "Always night" },
			{ "qol_cfg_timeweather_daylightpercent", "Daylight share" },
			{ "qol_cfg_timeweather_daylightpercent_desc", "How much of the 20-minute day/night cycle is day; the normal game is 70%. Higher means longer days and shorter nights. The clock and day count stay the same. On a server, sleeping still wakes you at the server's sunrise." },
			{ "qol_cfg_timeweather_weather", "Weather" },
			{ "qol_cfg_timeweather_weather_desc", "Force a weather on your game. Boss fights, raids, dungeons and the Mistlands keep their own weather." },
			{ "qol_cfg_timeweather_weather_normal", "Normal" },
			{ "qol_cfg_timeweather_weather_clear", "Clear" },
			{ "qol_cfg_timeweather_weather_lightrain", "Light rain" },
			{ "qol_cfg_timeweather_weather_rain", "Rain" },
			{ "qol_cfg_timeweather_weather_thunderstorm", "Thunderstorm" },
			{ "qol_cfg_timeweather_weather_snow", "Snow" },
			{ "qol_cfg_timeweather_weather_snowstorm", "Snowstorm" },
			{ "qol_cfg_timeweather_weather_misty", "Misty" },
			{ "qol_cfg_timeweather_nomistyweather", "No misty weather" },
			{ "qol_cfg_timeweather_nomistyweather_desc", "Misty weather, such as in the Meadows and Black Forest, never comes up. The Mistlands' own mist is not affected." },
			{ "qol_cfg_timeweather_windoverride", "Fixed wind" },
			{ "qol_cfg_timeweather_windoverride_desc", "Use the wind direction and strength below instead of the changing wind. This also drives sailing." },
			{ "qol_cfg_timeweather_winddirection", "Wind direction" },
			{ "qol_cfg_timeweather_winddirection_desc", "Compass direction the fixed wind blows toward: 0° north, 90° east, 180° south, 270° west." },
			{ "qol_cfg_timeweather_windstrength", "Wind strength" },
			{ "qol_cfg_timeweather_windstrength_desc", "Strength of the fixed wind." },
			{ "qol_unit_m", "$1 m" },
			{ "qol_unit_ms", "$1 ms" },
			{ "qol_unit_percent", "$1%" },
			{ "qol_unit_px", "$1 px" },
			{ "qol_unit_times", "$1x" },
			{ "qol_unit_degrees", "$1°" },
			{ "qol_key_leftshift", "Left Shift" },
			{ "qol_key_rightshift", "Right Shift" },
			{ "qol_key_leftalt", "Left Alt" },
			{ "qol_key_rightalt", "Right Alt" },
			{ "qol_key_leftcontrol", "Left Ctrl" },
			{ "qol_key_rightcontrol", "Right Ctrl" },
			{ "qol_key_mouse3", "Mouse button 4" },
			{ "qol_key_mouse4", "Mouse button 5" },
			{ "qol_key_none", "None" }
		};

		public static string Get(string key, params string[] args)
		{
			return Localization.instance.Localize("$" + key, args);
		}

		public static bool Has(string key)
		{
			return English.ContainsKey(key);
		}

		public static void Register(Localization localization, string language)
		{
			foreach (KeyValuePair<string, string> item in English)
			{
				localization.AddWord(item.Key, item.Value);
			}
			if (!string.IsNullOrEmpty(language) && language != "English")
			{
				foreach (KeyValuePair<string, string> item2 in LoadTranslation(language))
				{
					localization.AddWord(item2.Key, item2.Value);
				}
			}
			localization.m_cache.EvictAll();
		}

		private static Dictionary<string, string> LoadTranslation(string language)
		{
			Dictionary<string, string> dictionary = new Dictionary<string, string>();
			string text = Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)Plugin.Instance).Info.Location), "Translations", language + ".txt");
			if (!File.Exists(text))
			{
				return dictionary;
			}
			try
			{
				string[] array = File.ReadAllLines(text);
				for (int i = 0; i < array.Length; i++)
				{
					string text2 = array[i].Trim();
					if (text2.Length == 0 || text2.StartsWith("#"))
					{
						continue;
					}
					int num = text2.IndexOf('=');
					if (num > 0)
					{
						string key = text2.Substring(0, num).Trim();
						string text3 = text2.Substring(num + 1).Trim();
						if (English.ContainsKey(key) && text3.Length > 0)
						{
							dictionary[key] = text3;
						}
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not read translation file " + text + ": " + ex.Message));
			}
			return dictionary;
		}
	}
	public class ModSettingsTab : MonoBehaviour, ISettingsTab
	{
		private struct Range
		{
			public float Min;

			public float Max;

			public float Step;

			public string UnitKey;

			public Range(float min, float max, float step, string unitKey)
			{
				Min = min;
				Max = max;
				Step = step;
				UnitKey = unitKey;
			}
		}

		private class Feature
		{
			public string Section;

			public string Slug;

			public List<ConfigEntryBase> Entries = new List<ConfigEntryBase>();

			public Button Button;

			public GameObject Options;
		}

		private class Templates
		{
			public Toggle Toggle;

			public Slider Slider;

			public GameObject Stepper;
		}

		private const float ListWidth = 220f;

		private const float ListButtonHeight = 42f;

		private const float ListSpacing = 6f;

		private const float OptionsLeft = 250f;

		private const float LabelWidth = 330f;

		private const float ControlWidth = 220f;

		private const float RowHeight = 34f;

		private const float RowSpacing = 40f;

		private const float TopMargin = 12f;

		private const float BottomGap = 8f;

		private const float ScrollbarWidth = 10f;

		private static readonly Dictionary<string, Range> Ranges = new Dictionary<string, Range>
		{
			{
				"general/zoomoutonfoot",
				new Range(1f, 4f, 0.25f, "qol_unit_times")
			},
			{
				"general/zoomoutmounted",
				new Range(1f, 4f, 0.25f, "qol_unit_times")
			},
			{
				"general/zoomoutboat",
				new Range(1f, 4f, 0.25f, "qol_unit_times")
			},
			{
				"chestsstations/autopullrange",
				new Range(1f, 50f, 1f, "qol_unit_m")
			},
			{
				"chestsstations/quickstackrange",
				new Range(1f, 50f, 1f, "qol_unit_m")
			},
			{
				"chestsstations/restockrange",
				new Range(1f, 50f, 1f, "qol_unit_m")
			},
			{
				"crafting/queuebuttonoffsetx",
				new Range(-300f, 300f, 5f, "qol_unit_px")
			},
			{
				"crafting/queuebuttonoffsety",
				new Range(-300f, 300f, 5f, "qol_unit_px")
			},
			{
				"interface/playerstatsbuttonoffsetx",
				new Range(-300f, 300f, 5f, "qol_unit_px")
			},
			{
				"interface/playerstatsbuttonoffsety",
				new Range(-300f, 300f, 5f, "qol_unit_px")
			},
			{
				"interface/pingwarnms",
				new Range(20f, 500f, 10f, "qol_unit_ms")
			},
			{
				"interface/pingbadms",
				new Range(50f, 1000f, 10f, "qol_unit_ms")
			},
			{
				"interface/serverdelayminms",
				new Range(10f, 500f, 10f, "qol_unit_ms")
			},
			{
				"interface/packetlossminpercent",
				new Range(0.5f, 20f, 0.5f, "qol_unit_percent")
			},
			{
				"interface/netinfofontsize",
				new Range(10f, 30f, 1f, null)
			},
			{
				"interface/netinfooffsetx",
				new Range(-300f, 300f, 5f, "qol_unit_px")
			},
			{
				"interface/netinfooffsety",
				new Range(-300f, 300f, 5f, "qol_unit_px")
			},
			{
				"combatsurvival/autoeatthresholdpercent",
				new Range(1f, 50f, 1f, "qol_unit_percent")
			},
			{
				"cheats/skillgainmultiplier",
				new Range(1f, 10f, 1f, "qol_unit_times")
			},
			{
				"cheats/dropchancemultiplier",
				new Range(1f, 20f, 1f, "qol_unit_times")
			},
			{
				"cheats/trophychancemultiplier",
				new Range(1f, 20f, 1f, "qol_unit_times")
			},
			{
				"cheats/spawnratemultiplier",
				new Range(0.1f, 10f, 0.1f, "qol_unit_times")
			},
			{
				"cheats/spawnpopulationmultiplier",
				new Range(1f, 10f, 1f, "qol_unit_times")
			},
			{
				"cheats/spawngroupsizemultiplier",
				new Range(1f, 5f, 1f, "qol_unit_times")
			},
			{
				"building/arearepairradius",
				new Range(2f, 30f, 1f, "qol_unit_m")
			},
			{
				"timeweather/daylightpercent",
				new Range(10f, 95f, 5f, "qol_unit_percent")
			},
			{
				"timeweather/winddirection",
				new Range(0f, 345f, 15f, "qol_unit_degrees")
			},
			{
				"timeweather/windstrength",
				new Range(0f, 100f, 5f, "qol_unit_percent")
			}
		};

		private static readonly KeyCode[] KeyChoices;

		private static readonly Dictionary<string, KeyCode[]> KeyChoiceOverrides;

		private readonly List<Feature> _features = new List<Feature>();

		private readonly List<Action> _showValues = new List<Action>();

		private readonly Dictionary<ConfigEntryBase, object> _pending = new Dictionary<ConfigEntryBase, object>();

		private int _selected;

		private ScrollRect _listScroll;

		private ScrollRect _optionsScroll;

		public event Action<string, int> SharedSettingChanged
		{
			add
			{
			}
			remove
			{
			}
		}

		public static void Install(Settings settings)
		{
			//IL_0251: Unknown result type (might be due to invalid IL or missing references)
			//IL_0256: Unknown result type (might be due to invalid IL or missing references)
			//IL_025e: Unknown result type (might be due to invalid IL or missing references)
			//IL_026a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0274: Expected O, but got Unknown
			//IL_0274: Unknown result type (might be due to invalid IL or missing references)
			//IL_027b: Unknown result type (might be due to invalid IL or missing references)
			//IL_027c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0286: Expected O, but got Unknown
			//IL_028b: Expected O, but got Unknown
			TabHandler val = ((IEnumerable<TabHandler>)((Component)settings).GetComponentsInChildren<TabHandler>(true)).FirstOrDefault((Func<TabHandler, bool>)((TabHandler h) => h.m_tabs.Any((Tab t) => (Object)(object)t.m_page != (Object)null && (Object)(object)((Component)t.m_page).GetComponent<GameplaySettings>() != (Object)null)));
			if ((Object)(object)val == (Object)null)
			{
				Plugin.Log.LogWarning((object)"[Settings] Settings tab bar not found; QoL tab not added.");
			}
			else
			{
				if (val.m_tabs.Any((Tab t) => (Object)(object)t.m_page != (Object)null && (Object)(object)((Component)t.m_page).GetComponent<ModSettingsTab>() != (Object)null))
				{
					return;
				}
				Tab val2 = val.m_tabs.First((Tab t) => (Object)(object)t.m_page != (Object)null && (Object)(object)((Component)t.m_page).GetComponent<GameplaySettings>() != (Object)null);
				Button val3 = val.m_tabs.Select((Tab t) => t.m_button).FirstOrDefault((Func<Button, bool>)((Button b) => (Object)(object)b != (Object)null && (Object)(object)((Component)b).transform.Find("KeyHint") == (Object)null));
				if ((Object)(object)val3 == (Object)null)
				{
					Plugin.Log.LogWarning((object)"[Settings] No settings tab button to copy; QoL tab not added.");
					return;
				}
				Button val4 = CopyButton(val3, ((Component)val3).transform.parent, "QoL", "$qol_settings_tab");
				((Component)val4).transform.SetAsLastSibling();
				Button val5 = val.m_tabs.Select((Tab t) => t.m_button).LastOrDefault((Func<Button, bool>)((Button b) => (Object)(object)b != (Object)null));
				Transform val6 = (((Object)(object)val5 != (Object)null) ? ((Component)val5).transform.Find("KeyHint") : null);
				if ((Object)(object)val6 != (Object)null)
				{
					val6.SetParent(((Component)val4).transform, false);
				}
				GameObject gameObject = ((Component)val2.m_page).gameObject;
				bool activeSelf = gameObject.activeSelf;
				gameObject.SetActive(false);
				GameObject page = Object.Instantiate<GameObject>(gameObject, gameObject.transform.parent, false);
				gameObject.SetActive(activeSelf);
				((Object)page).name = "QoL";
				Object.DestroyImmediate((Object)(object)page.GetComponent<GameplaySettings>());
				ModSettingsTab modSettingsTab = page.AddComponent<ModSettingsTab>();
				Scrollbar scrollbarTemplate = ((IEnumerable<Scrollbar>)((Component)settings).GetComponentsInChildren<Scrollbar>(true)).FirstOrDefault((Func<Scrollbar, bool>)((Scrollbar b) => !((Component)b).transform.IsChildOf(page.transform)));
				modSettingsTab.Build(val3, scrollbarTemplate);
				val.m_tabs.Add(new Tab
				{
					m_button = val4,
					m_page = (RectTransform)page.transform,
					m_default = false,
					m_onClick = new UnityEvent()
				});
			}
		}

		private void Build(Button buttonTemplate, Scrollbar scrollbarTemplate)
		{
			//IL_0204: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Expected O, but got Unknown
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Expected O, but got Unknown
			//IL_016f: Unknown result type (might be due to invalid IL or missing references)
			Templates templates = TakeTemplates();
			if (templates == null)
			{
				return;
			}
			_listScro