Decompiled source of Base Volume Control v1.2.0

BaseVolumeControl.dll

Decompiled 20 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.UI;
using Valheim.SettingsGui;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("BaseVolumeControl")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.2.0.0")]
[assembly: AssemblyInformationalVersion("1.2.0")]
[assembly: AssemblyProduct("BaseVolumeControl")]
[assembly: AssemblyTitle("BaseVolumeControl")]
[assembly: AssemblyVersion("1.2.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace BaseVolumeControl
{
	[BepInPlugin("BaseVolumeControl", "Base Volume Control", "1.2.0")]
	[BepInProcess("valheim.exe")]
	public class BaseVolumeControlPlugin : BaseUnityPlugin
	{
		private static class SoundPatches
		{
			[HarmonyPostfix]
			[HarmonyPatch(typeof(ZSFX), "Awake")]
			private static void SfxAwake(ZSFX __instance)
			{
				Piece componentInParent = ((Component)__instance).GetComponentInParent<Piece>();
				if ((Object)(object)componentInParent != (Object)null)
				{
					Sounds.TrackSfx(__instance, Category.Of((Component)(object)componentInParent));
				}
			}

			[HarmonyPostfix]
			[HarmonyPatch(typeof(Windmill), "Start")]
			private static void WindmillStart(Windmill __instance)
			{
				AudioSource[] array = __instance.m_sfxLoops ?? Array.Empty<AudioSource>();
				AudioSource[] array2 = array;
				foreach (AudioSource val in array2)
				{
					if ((Object)(object)val != (Object)null && (Object)(object)((Component)val).GetComponent<ZSFX>() != (Object)null)
					{
						return;
					}
				}
				Sounds.Track((Object)(object)__instance, __instance.m_maxVol, array, Category.Windmill);
			}

			[HarmonyPostfix]
			[HarmonyPatch(typeof(ShieldGenerator), "Start")]
			private static void ShieldStart(ShieldGenerator __instance)
			{
				Sounds.Own(__instance.m_shieldHitEffects, Category.ShieldHits);
				Sounds.Own(__instance.m_shieldLowLoop, Category.ShieldHum);
				Sounds.Own(__instance.m_shieldStart, Category.ShieldHum);
				Sounds.Own(__instance.m_shieldStop, Category.ShieldHum);
				if ((Object)(object)__instance.m_shieldDome != (Object)null)
				{
					ZSFX[] componentsInChildren = __instance.m_shieldDome.GetComponentsInChildren<ZSFX>(true);
					for (int i = 0; i < componentsInChildren.Length; i++)
					{
						Sounds.TrackSfx(componentsInChildren[i], Category.ShieldHum);
					}
				}
			}

			[HarmonyPostfix]
			[HarmonyPatch(typeof(Smelter), "Awake")]
			private static void SmelterAwake(Smelter __instance)
			{
				Sounds.Own(__instance.m_produceEffects, Category.Of((Component)(object)__instance));
			}

			[HarmonyPostfix]
			[HarmonyPatch(typeof(EffectList), "Create")]
			private static void EffectCreate(EffectList __instance, GameObject[] __result)
			{
				Sounds.Spawned(__instance, __result);
			}
		}

		private static class MenuPatches
		{
			[HarmonyPostfix]
			[HarmonyPatch(typeof(Menu), "Start")]
			private static void MenuStart(Menu __instance)
			{
				//IL_0076: Unknown result type (might be due to invalid IL or missing references)
				//IL_0080: Expected O, but got Unknown
				//IL_008d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0097: Expected O, but got Unknown
				try
				{
					Button settingsButton = __instance.m_settingsButton;
					if ((Object)(object)settingsButton == (Object)null)
					{
						Log.LogWarning((object)"The game's Settings button was not found, no menu button added.");
						return;
					}
					Button component = Object.Instantiate<GameObject>(((Component)settingsButton).gameObject, ((Component)settingsButton).transform.parent, false).GetComponent<Button>();
					((Object)component).name = "BaseVolumeControl_Button";
					((Component)component).transform.SetSiblingIndex(((Component)settingsButton).transform.GetSiblingIndex() + 1);
					component.onClick = new ButtonClickedEvent();
					((UnityEvent)component.onClick).AddListener((UnityAction)delegate
					{
						SettingsWindow.OpenFromMenu(__instance);
					});
					TMP_Text componentInChildren = ((Component)component).GetComponentInChildren<TMP_Text>(true);
					if ((Object)(object)componentInChildren != (Object)null)
					{
						componentInChildren.text = Tr.T("Building sounds");
					}
				}
				catch (Exception ex)
				{
					Log.LogError((object)("Could not add the menu button: " + ex));
				}
			}

			[HarmonyPrefix]
			[HarmonyPatch(typeof(Menu), "Update")]
			private static bool MenuUpdate()
			{
				return (Object)(object)SettingsWindow.Current == (Object)null;
			}

			[HarmonyPostfix]
			[HarmonyPatch(typeof(Menu), "Hide")]
			private static void MenuHide()
			{
				if ((Object)(object)SettingsWindow.Current != (Object)null)
				{
					SettingsWindow.Current.Cancel();
				}
			}
		}

		public const string Guid = "BaseVolumeControl";

		public const string Name = "Base Volume Control";

		public const string Version = "1.2.0";

		internal static ConfigEntry<bool> ModEnabled;

		internal static ConfigEntry<int> MasterVolume;

		internal static ConfigEntry<KeyboardShortcut> MuteKey;

		internal static BaseVolumeControlPlugin Instance;

		internal static ManualLogSource Log;

		private static bool muted;

		private static bool dirty;

		private void Awake()
		{
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Expected O, but got Unknown
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
			ModEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "Enabled", true, "Apply the settings below. Off plays every building at its normal volume and range (the mute hotkey still works).");
			MasterVolume = ((BaseUnityPlugin)this).Config.Bind<int>("1 - General", "All buildings volume", 100, new ConfigDescription("Volume of every building sound in percent, on top of each building's own volume.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>()));
			MuteKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("1 - General", "Mute hotkey", new KeyboardShortcut((KeyCode)302, Array.Empty<KeyCode>()), "Press to silence every building sound, press again to bring them back.");
			for (int i = 0; i < Category.All.Length; i++)
			{
				Category.All[i].Bind(((BaseUnityPlugin)this).Config, i + 2);
			}
			((BaseUnityPlugin)this).Config.SettingChanged += delegate
			{
				dirty = true;
			};
			DropOrphans();
			((BaseUnityPlugin)this).Config.Save();
			Harmony harmony = new Harmony("BaseVolumeControl");
			PatchSafely(harmony, typeof(SoundPatches), "building sounds");
			PatchSafely(harmony, typeof(MenuPatches), "pause menu button");
			if (SelfTest.Requested)
			{
				PatchSafely(harmony, typeof(SelfTest), "self-test");
			}
		}

		private void PatchSafely(Harmony harmony, Type patches, string what)
		{
			try
			{
				harmony.PatchAll(patches);
			}
			catch (Exception arg)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)$"Could not hook the {what}, that part is off: {arg}");
			}
		}

		private void DropOrphans()
		{
			try
			{
				(AccessTools.Property(typeof(ConfigFile), "OrphanedEntries")?.GetValue(((BaseUnityPlugin)this).Config) as IDictionary)?.Clear();
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not drop old settings from the cfg: " + ex.Message));
			}
		}

		internal static IEnumerable<ConfigEntryBase> AllEntries()
		{
			yield return (ConfigEntryBase)(object)ModEnabled;
			yield return (ConfigEntryBase)(object)MasterVolume;
			yield return (ConfigEntryBase)(object)MuteKey;
			Category[] all = Category.All;
			foreach (Category c in all)
			{
				yield return (ConfigEntryBase)(object)c.VolumeEntry;
				yield return (ConfigEntryBase)(object)c.RangeEntry;
			}
		}

		internal static void SaveConfig()
		{
			((BaseUnityPlugin)Instance).Config.Save();
		}

		internal static Dictionary<ConfigEntryBase, object> Snapshot()
		{
			Dictionary<ConfigEntryBase, object> dictionary = new Dictionary<ConfigEntryBase, object>();
			foreach (ConfigEntryBase item in AllEntries())
			{
				dictionary[item] = item.BoxedValue;
			}
			return dictionary;
		}

		internal static bool ChangedSince(Dictionary<ConfigEntryBase, object> snapshot)
		{
			foreach (ConfigEntryBase item in AllEntries())
			{
				if (!snapshot.TryGetValue(item, out var value) || !object.Equals(item.BoxedValue, value))
				{
					return true;
				}
			}
			return false;
		}

		internal static void Restore(Dictionary<ConfigEntryBase, object> snapshot)
		{
			foreach (KeyValuePair<ConfigEntryBase, object> item in snapshot)
			{
				item.Key.BoxedValue = item.Value;
			}
		}

		internal static void ResetToDefaults()
		{
			foreach (ConfigEntryBase item in AllEntries())
			{
				item.BoxedValue = item.DefaultValue;
			}
		}

		private void Update()
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			if (dirty)
			{
				dirty = false;
				Sounds.ApplyAll();
			}
			if ((Object)(object)SettingsWindow.Current != (Object)null)
			{
				return;
			}
			KeyboardShortcut value = MuteKey.Value;
			if (((KeyboardShortcut)(ref value)).IsDown() && !Typing())
			{
				muted = !muted;
				Sounds.ApplyAll();
				Player localPlayer = Player.m_localPlayer;
				if (localPlayer != null)
				{
					((Character)localPlayer).Message((MessageType)1, Tr.T(muted ? "Building sounds muted" : "Building sounds back"), 0, (Sprite)null, false);
				}
			}
		}

		private static bool Typing()
		{
			if ((!((Object)(object)Chat.instance != (Object)null) || !Chat.instance.HasFocus()) && !Console.IsVisible())
			{
				return TextInput.IsVisible();
			}
			return true;
		}

		internal static float VolumeFactor(Category c)
		{
			if (!muted)
			{
				if (ModEnabled.Value)
				{
					return (float)MasterVolume.Value / 100f * (float)c.VolumeEntry.Value / 100f;
				}
				return 1f;
			}
			return 0f;
		}

		internal static float RangeFactor(Category c)
		{
			if (!ModEnabled.Value)
			{
				return 1f;
			}
			return (float)c.RangeEntry.Value / 100f;
		}
	}
	internal static class Sounds
	{
		private class Entry
		{
			public Object target;

			public float volume;

			public AudioSource[] sources;

			public float[] minDistance;

			public float[] maxDistance;

			public Category category;
		}

		private static readonly Dictionary<int, Entry> tracked = new Dictionary<int, Entry>();

		private static readonly List<int> dead = new List<int>();

		private static int addedSincePurge;

		private static readonly ConditionalWeakTable<EffectList, Category> effectOwners = new ConditionalWeakTable<EffectList, Category>();

		internal static void Own(EffectList list, Category category)
		{
			if (list != null)
			{
				effectOwners.Remove(list);
				effectOwners.Add(list, category);
			}
		}

		internal static void Spawned(EffectList list, GameObject[] created)
		{
			if (created == null || !effectOwners.TryGetValue(list, out var value))
			{
				return;
			}
			foreach (GameObject val in created)
			{
				if ((Object)(object)val != (Object)null)
				{
					ZSFX[] componentsInChildren = val.GetComponentsInChildren<ZSFX>(true);
					for (int j = 0; j < componentsInChildren.Length; j++)
					{
						TrackSfx(componentsInChildren[j], value);
					}
				}
			}
		}

		internal static void TrackSfx(ZSFX sfx, Category category)
		{
			AudioSource component = ((Component)sfx).GetComponent<AudioSource>();
			Track((Object)(object)sfx, sfx.GetVolumeModifier(), (AudioSource[])((!((Object)(object)component != (Object)null)) ? ((Array)Array.Empty<AudioSource>()) : ((Array)new AudioSource[1] { component })), category);
		}

		internal static void Track(Object target, float volume, AudioSource[] sources, Category category)
		{
			int instanceID = target.GetInstanceID();
			if (!tracked.TryGetValue(instanceID, out var value))
			{
				if (++addedSincePurge >= 512)
				{
					Purge();
				}
				value = new Entry
				{
					target = target,
					volume = volume,
					sources = sources,
					minDistance = new float[sources.Length],
					maxDistance = new float[sources.Length]
				};
				for (int i = 0; i < sources.Length; i++)
				{
					if ((Object)(object)sources[i] != (Object)null)
					{
						value.minDistance[i] = sources[i].minDistance;
						value.maxDistance[i] = sources[i].maxDistance;
					}
				}
				tracked[instanceID] = value;
			}
			value.category = category;
			Apply(value);
		}

		private static void Apply(Entry e)
		{
			float num = e.volume * BaseVolumeControlPlugin.VolumeFactor(e.category);
			Object target = e.target;
			ZSFX val = (ZSFX)(object)((target is ZSFX) ? target : null);
			if (val == null)
			{
				Windmill val2 = (Windmill)(object)((target is Windmill) ? target : null);
				if (val2 != null)
				{
					val2.m_maxVol = num;
				}
			}
			else
			{
				val.SetVolumeModifier(num);
			}
			float num2 = BaseVolumeControlPlugin.RangeFactor(e.category);
			for (int i = 0; i < e.sources.Length; i++)
			{
				AudioSource val3 = e.sources[i];
				if (!((Object)(object)val3 == (Object)null))
				{
					val3.minDistance = e.minDistance[i] * num2;
					val3.maxDistance = e.maxDistance[i] * num2;
				}
			}
		}

		private static void Purge()
		{
			addedSincePurge = 0;
			dead.Clear();
			foreach (KeyValuePair<int, Entry> item in tracked)
			{
				if (item.Value.target == (Object)null)
				{
					dead.Add(item.Key);
				}
			}
			foreach (int item2 in dead)
			{
				tracked.Remove(item2);
			}
		}

		internal static void ApplyAll()
		{
			Purge();
			foreach (Entry value in tracked.Values)
			{
				Apply(value);
			}
		}
	}
	internal enum Group
	{
		Protection,
		Travel,
		Production,
		FarmAndKitchen,
		FireAndLight,
		EverythingElse
	}
	internal class Category
	{
		public readonly string En;

		public readonly string HelpEn;

		public readonly Group Group;

		private readonly string gameName;

		private readonly int defaultVolume;

		private readonly string cfgName;

		private readonly string[] prefabs;

		public ConfigEntry<int> VolumeEntry;

		public ConfigEntry<int> RangeEntry;

		public static readonly Category ShieldHum = new Category("Shield generator hum", Group.Protection, "The constant hum of the shield generator and its dome, its start and stop, and the low fuel warning.", null, 0, null, "piece_shieldgenerator");

		public static readonly Category ShieldHits = new Category("Shield generator hits", Group.Protection, "The sound of enemies and projectiles hitting the dome.", null, 100, null);

		public static readonly Category Portals = new Category("Portals", Group.Travel, "The hum of portals.", null, 100, null);

		public static readonly Category Smelter = new Category("Smelter", Group.Production, "The roar of the smelter and the sound of each finished bar.", "piece_smelter", 100, null, "smelter");

		public static readonly Category BlastFurnace = new Category("Blast furnace", Group.Production, "The roar of the blast furnace and the sound of each finished bar.", "piece_blastfurnace", 100, null, "blastfurnace");

		public static readonly Category Kiln = new Category("Charcoal kiln", Group.Production, "The fire of the charcoal kiln and the sound of each piece of coal.", "piece_charcoalkiln", 100, null, "charcoal_kiln");

		public static readonly Category Eitr = new Category("Eitr refinery", Group.Production, "The eitr refinery at work and the sound of each finished eitr.", "piece_eitrrefinery", 100, null, "eitrrefinery");

		public static readonly Category Spinning = new Category("Spinning wheel", Group.Production, "The spinning wheel turning and the sound of each finished thread.", "piece_spinningwheel", 100, null, "piece_spinningwheel");

		public static readonly Category Windmill = new Category("Windmill", Group.Production, "The creaking sails of the windmill and the sound of each finished flour.", "piece_windmill", 100, null, "windmill");

		public static readonly Category Beehives = new Category("Beehives and nests", Group.FarmAndKitchen, "The buzzing of bees in beehives and the birds of bird nests.", null, 100, "Beehives", "piece_birdnest");

		public static readonly Category Fermenters = new Category("Fermenters", Group.FarmAndKitchen, "The bubbling of fermenters.", null, 100, null);

		public static readonly Category Sap = new Category("Sap extractors", Group.FarmAndKitchen, "Sap extractors on Yggdrasil roots.", null, 100, null);

		public static readonly Category Obliterators = new Category("Obliterators", Group.Production, "The obliterator and its lightning.", null, 100, null);

		public static readonly Category Ships = new Category("Ships", Group.Travel, "Creaking hulls, sails and water around ships, also while sailing.", null, 100, null);

		public static readonly Category Fires = new Category("Fires and torches", Group.FireAndLight, "The crackle of campfires, hearths, braziers, torches and sconces.", null, 100, null, "piece_EternalPyre");

		public static readonly Category Cooking = new Category("Cooking stations", Group.FarmAndKitchen, "Food sizzling on cooking stations and in the oven.", null, 100, null);

		public static readonly Category Other = new Category("Other buildings", Group.EverythingElse, "Constant sounds of any other building that is not in the list above.", null, 100, null);

		public static readonly Category Crafting = new Category("Forges and tables", Group.Production, "The hum and fire of the forge, the black forge and the galdr table.", null, 100, null, "forge", "blackforge", "piece_magetable");

		public static readonly Category Frost = new Category("Frost foundry and kiln", Group.Production, "The frost foundry and the frost kiln at work.", null, 100, null, "piece_FrostFoundry", "piece_FrostKiln");

		public static readonly Category Wards = new Category("Wards", Group.Protection, "The hum of an active ward.", null, 100, null, "guard_stone", "dverger_guardstone");

		public static readonly Category Cauldrons = new Category("Cauldrons", Group.FarmAndKitchen, "Bubbling cauldrons and mead cauldrons over the fire.", null, 100, null, "piece_cauldron", "piece_MeadCauldron");

		public static readonly Category HotTub = new Category("Hot tub", Group.EverythingElse, "The water and the fire of the hot tub.", "piece_bathtub", 100, null, "piece_bathtub");

		public static readonly Category Carts = new Category("Carts and sleds", Group.Travel, "Wheels and runners of carts and sleds.", null, 100, null, "Cart", "Sled");

		public static readonly Category Siege = new Category("Siege engines", Group.Protection, "The battering ram and the catapult.", null, 100, null, "BatteringRam", "Catapult");

		public static readonly Category[] All = new Category[24]
		{
			ShieldHum, ShieldHits, Portals, Smelter, BlastFurnace, Kiln, Eitr, Spinning, Windmill, Beehives,
			Fermenters, Sap, Obliterators, Ships, Fires, Cooking, Other, Crafting, Frost, Wards,
			Cauldrons, HotTub, Carts, Siege
		};

		public static readonly (Group group, string en)[] Groups = new(Group, string)[6]
		{
			(Group.Protection, "Protection"),
			(Group.Travel, "Travel"),
			(Group.Production, "Production"),
			(Group.FarmAndKitchen, "Farm and kitchen"),
			(Group.FireAndLight, "Fire and light"),
			(Group.EverythingElse, "Everything else")
		};

		private static readonly (Type component, Category category)[] byComponent = new(Type, Category)[10]
		{
			(typeof(ShieldGenerator), ShieldHum),
			(typeof(TeleportWorld), Portals),
			(typeof(PrivateArea), Wards),
			(typeof(Beehive), Beehives),
			(typeof(Fermenter), Fermenters),
			(typeof(SapCollector), Sap),
			(typeof(Incinerator), Obliterators),
			(typeof(Ship), Ships),
			(typeof(Fireplace), Fires),
			(typeof(CookingStation), Cooking)
		};

		private static Dictionary<string, Category> byPrefab;

		public string Name
		{
			get
			{
				if (gameName == null)
				{
					return Tr.T(En);
				}
				return Tr.GameText(gameName, En);
			}
		}

		public string Help => Tr.T(HelpEn);

		private Category(string en, Group group, string helpEn, string gameName = null, int volume = 100, string cfgName = null, params string[] prefabs)
		{
			En = en;
			Group = group;
			HelpEn = helpEn;
			this.gameName = gameName;
			defaultVolume = volume;
			this.cfgName = cfgName ?? en;
			this.prefabs = prefabs;
		}

		public void Bind(ConfigFile config, int number)
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Expected O, but got Unknown
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Expected O, but got Unknown
			string text = $"{number:00} - {cfgName}";
			VolumeEntry = config.Bind<int>(text, "Volume", defaultVolume, new ConfigDescription("Volume of " + En.ToLowerInvariant() + " in percent (0 = silent, 100 = normal).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>()));
			RangeEntry = config.Bind<int>(text, "Hearing range", 100, new ConfigDescription("How far " + En.ToLowerInvariant() + " can be heard, in percent of the normal distance.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(10, 100), Array.Empty<object>()));
		}

		public static Category Of(Component building)
		{
			if (byPrefab == null)
			{
				byPrefab = new Dictionary<string, Category>();
				Category[] all = All;
				foreach (Category category in all)
				{
					string[] array = category.prefabs;
					foreach (string key in array)
					{
						byPrefab[key] = category;
					}
				}
			}
			if (byPrefab.TryGetValue(PrefabName(building.gameObject), out var value))
			{
				return value;
			}
			(Type, Category)[] array2 = byComponent;
			for (int i = 0; i < array2.Length; i++)
			{
				var (type, result) = array2[i];
				if ((Object)(object)building.GetComponent(type) != (Object)null)
				{
					return result;
				}
			}
			return Other;
		}

		private static string PrefabName(GameObject go)
		{
			string name = ((Object)go).name;
			int num = name.IndexOf("(Clone)", StringComparison.Ordinal);
			if (num < 0)
			{
				return name;
			}
			return name.Substring(0, num).TrimEnd();
		}
	}
	internal static class SelfTest
	{
		private static readonly string shot = Environment.GetEnvironmentVariable("BVC_SELFTEST");

		private static readonly string language = Environment.GetEnvironmentVariable("BVC_SELFTEST_LANG");

		public static bool Requested => !string.IsNullOrEmpty(shot);

		[HarmonyPostfix]
		[HarmonyPatch(typeof(FejdStartup), "Start")]
		private static void StartupStart(FejdStartup __instance)
		{
			((MonoBehaviour)BaseVolumeControlPlugin.Instance).StartCoroutine(Run(__instance));
		}

		private static IEnumerator Run(FejdStartup startup)
		{
			ManualLogSource log = BaseVolumeControlPlugin.Log;
			yield return (object)new WaitForSeconds(8f);
			DumpLocalization(log);
			string playerLanguage = Localization.instance.GetSelectedLanguage();
			if (!string.IsNullOrEmpty(language))
			{
				Localization.instance.SetLanguage(language);
			}
			SettingsWindow.Templates templates = SettingsWindow.Templates.From(startup.m_settingsPrefab);
			log.LogInfo((object)("SELFTEST templates " + ((templates == null) ? "missing" : "found")));
			SettingsWindow window = null;
			if (templates != null)
			{
				startup.m_mainMenu.SetActive(false);
				window = SettingsWindow.Create(((Component)startup.m_mainMenu.GetComponentInParent<Canvas>().rootCanvas).transform, templates);
				log.LogInfo((object)"SELFTEST window built");
			}
			yield return (object)new WaitForSeconds(2f);
			if ((Object)(object)window != (Object)null)
			{
				window.ShowHelp(Category.ShieldHum.Help);
				BaseVolumeControlPlugin.MasterVolume.Value = 80;
				window.UpdateStatus();
			}
			yield return null;
			ScreenCapture.CaptureScreenshot(shot);
			yield return (object)new WaitForSeconds(1f);
			ScrollRect val = (((Object)(object)window != (Object)null) ? ((Component)window).GetComponentInChildren<ScrollRect>() : null);
			if ((Object)(object)val != (Object)null)
			{
				val.verticalNormalizedPosition = 0f;
			}
			yield return null;
			ScreenCapture.CaptureScreenshot(shot.Replace(".png", "_bottom.png"));
			log.LogInfo((object)"SELFTEST screenshots saved");
			yield return (object)new WaitForSeconds(2f);
			Localization.instance.SetLanguage(playerLanguage);
			Application.Quit();
		}

		private static void DumpLocalization(ManualLogSource log)
		{
			string environmentVariable = Environment.GetEnvironmentVariable("BVC_DUMPLOC");
			if (string.IsNullOrEmpty(environmentVariable))
			{
				return;
			}
			Localization instance = Localization.instance;
			string selectedLanguage = instance.GetSelectedLanguage();
			log.LogInfo((object)("LOC languages: " + string.Join(",", instance.GetLanguages())));
			foreach (string language in instance.GetLanguages())
			{
				instance.SetLanguage(language);
				string[] array = environmentVariable.Split(',');
				foreach (string text in array)
				{
					log.LogInfo((object)("LOC " + language + " | " + text + " | " + instance.Localize("$" + text)));
				}
			}
			instance.SetLanguage(selectedLanguage);
		}
	}
	internal class SettingsWindow : MonoBehaviour
	{
		internal class Templates
		{
			public Slider slider;

			public TMP_Text text;

			public Toggle toggle;

			public Button button;

			public Image panel;

			public Scrollbar scrollbar;

			public static Templates From(GameObject settingsPrefab)
			{
				if ((Object)(object)settingsPrefab == (Object)null)
				{
					return null;
				}
				AudioSettings componentInChildren = settingsPrefab.GetComponentInChildren<AudioSettings>(true);
				Settings componentInChildren2 = settingsPrefab.GetComponentInChildren<Settings>(true);
				if ((Object)(object)componentInChildren == (Object)null || (Object)(object)componentInChildren2 == (Object)null)
				{
					return null;
				}
				Slider val = Field<Slider>(componentInChildren, "m_sfxVolumeSlider");
				TMP_Text val2 = Field<TMP_Text>(componentInChildren, "m_sfxVolumeText");
				Button val3 = Field<Button>(componentInChildren2, "m_backButton");
				if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null || (Object)(object)val3 == (Object)null)
				{
					return null;
				}
				GameObject val4 = Field<GameObject>(componentInChildren2, "m_settingsPanel");
				return new Templates
				{
					slider = val,
					text = val2,
					toggle = Field<Toggle>(componentInChildren, "m_continousMusic"),
					button = val3,
					panel = (((Object)(object)val4 != (Object)null) ? val4.GetComponent<Image>() : null),
					scrollbar = settingsPrefab.GetComponentInChildren<Scrollbar>(true)
				};
			}

			private static T Field<T>(object owner, string name) where T : class
			{
				return AccessTools.Field(owner.GetType(), name)?.GetValue(owner) as T;
			}
		}

		private class RowHover : MonoBehaviour, IPointerEnterHandler, IEventSystemHandler, IPointerExitHandler
		{
			public SettingsWindow window;

			public string text;

			public Image image;

			public void OnPointerEnter(PointerEventData _)
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				((Graphic)image).color = Highlight;
				window.ShowHelp(text);
			}

			public void OnPointerExit(PointerEventData _)
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				((Graphic)image).color = Color.clear;
				window.ShowHelp(null);
			}
		}

		private static readonly Color Orange = new Color(1f, 0.631f, 0.235f);

		private static readonly Color HelpColor = new Color(0.86f, 0.82f, 0.74f);

		private static readonly Color Highlight = new Color(1f, 0.8f, 0.5f, 0.08f);

		private static readonly Vector2 WindowSize = new Vector2(1080f, 920f);

		private const float LabelWidth = 280f;

		private const float SliderWidth = 240f;

		private const float ValueWidth = 60f;

		private const float Gap = 16f;

		private const float RowHeight = 38f;

		private const float RowsPerNotch = 2f;

		private Templates t;

		private Action onClosed;

		private Dictionary<ConfigEntryBase, object> opened;

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

		private TMP_Text help;

		private TMP_Text status;

		private TMP_Text keyText;

		private string helpDefault;

		private bool waitingForKey;

		private static KeyCode[] allKeys;

		private RectTransform listArea;

		private ScrollRect listScroll;

		private Camera canvasCamera;

		private float wheelTarget = -1f;

		public static SettingsWindow Current { get; private set; }

		public static void OpenFromMenu(Menu menu)
		{
			if ((Object)(object)Current != (Object)null)
			{
				return;
			}
			Templates templates = Templates.From(menu.m_settingsPrefab);
			if (templates == null)
			{
				BaseVolumeControlPlugin.Log.LogError((object)"The game's settings window has changed, the window cannot be built.");
				return;
			}
			try
			{
				Create(menu.m_root, templates, delegate
				{
					if ((Object)(object)menu != (Object)null && (Object)(object)menu.m_menuDialog != (Object)null)
					{
						((Component)menu.m_menuDialog).gameObject.SetActive(true);
					}
				});
				((Component)menu.m_menuDialog).gameObject.SetActive(false);
			}
			catch (Exception ex)
			{
				BaseVolumeControlPlugin.Log.LogError((object)("Could not build the settings window: " + ex));
				if ((Object)(object)Current != (Object)null)
				{
					Object.Destroy((Object)(object)((Component)Current).gameObject);
				}
				((Component)menu.m_menuDialog).gameObject.SetActive(true);
			}
		}

		public static SettingsWindow Create(Transform parent, Templates templates, Action onClosed = null)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("BaseVolumeControl_Window", new Type[1] { typeof(RectTransform) });
			val.transform.SetParent(parent, false);
			SettingsWindow settingsWindow = val.AddComponent<SettingsWindow>();
			settingsWindow.t = templates;
			settingsWindow.onClosed = onClosed;
			Current = settingsWindow;
			settingsWindow.Build();
			return settingsWindow;
		}

		public void Save()
		{
			bool num = BaseVolumeControlPlugin.ChangedSince(opened);
			BaseVolumeControlPlugin.SaveConfig();
			CloseWindow();
			if (num)
			{
				MessageHud instance = MessageHud.instance;
				if (instance != null)
				{
					instance.ShowMessage((MessageType)1, Tr.T("Building sounds saved"), 0, (Sprite)null, false, true);
				}
			}
		}

		public void Cancel()
		{
			BaseVolumeControlPlugin.Restore(opened);
			CloseWindow();
		}

		private void CloseWindow()
		{
			if ((Object)(object)Current == (Object)(object)this)
			{
				Current = null;
			}
			Object.Destroy((Object)(object)((Component)this).gameObject);
			onClosed?.Invoke();
		}

		private void OnDestroy()
		{
			if ((Object)(object)Current == (Object)(object)this)
			{
				Current = null;
			}
		}

		private void Build()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Expected O, but got Unknown
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_019e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c6: Expected O, but got Unknown
			//IL_0237: Unknown result type (might be due to invalid IL or missing references)
			//IL_05d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_061a: Unknown result type (might be due to invalid IL or missing references)
			//IL_048a: Unknown result type (might be due to invalid IL or missing references)
			RectTransform val = (RectTransform)((Component)this).transform;
			Vector2 val2 = default(Vector2);
			((Vector2)(ref val2))..ctor(0.5f, 0.5f);
			val.pivot = val2;
			Vector2 anchorMin = (val.anchorMax = val2);
			val.anchorMin = anchorMin;
			val.sizeDelta = WindowSize;
			Rect rect = ((RectTransform)((Component)this).transform.parent).rect;
			Vector2 val4 = ((Rect)(ref rect)).size - new Vector2(40f, 40f);
			float num = Mathf.Min(new float[3]
			{
				1f,
				val4.x / WindowSize.x,
				val4.y / WindowSize.y
			});
			if (num > 0f)
			{
				((Transform)val).localScale = new Vector3(num, num, 1f);
			}
			Canvas componentInParent = ((Component)this).GetComponentInParent<Canvas>();
			canvasCamera = (((Object)(object)componentInParent != (Object)null && (int)componentInParent.renderMode != 0) ? componentInParent.worldCamera : null);
			Image val5 = ((Component)this).gameObject.AddComponent<Image>();
			if ((Object)(object)t.panel != (Object)null)
			{
				val5.sprite = t.panel.sprite;
				val5.type = t.panel.type;
				((Graphic)val5).color = ((Graphic)t.panel).color;
				((Graphic)val5).material = ((Graphic)t.panel).material;
				val5.pixelsPerUnitMultiplier = t.panel.pixelsPerUnitMultiplier;
			}
			else
			{
				((Graphic)val5).color = new Color(0.12f, 0.1f, 0.08f, 0.96f);
			}
			VerticalLayoutGroup obj = ((Component)this).gameObject.AddComponent<VerticalLayoutGroup>();
			((LayoutGroup)obj).padding = new RectOffset(48, 48, 32, 32);
			((HorizontalOrVerticalLayoutGroup)obj).spacing = 10f;
			bool childControlWidth = (((HorizontalOrVerticalLayoutGroup)obj).childControlHeight = true);
			((HorizontalOrVerticalLayoutGroup)obj).childControlWidth = childControlWidth;
			((HorizontalOrVerticalLayoutGroup)obj).childForceExpandWidth = true;
			((HorizontalOrVerticalLayoutGroup)obj).childForceExpandHeight = false;
			TMP_Text obj2 = Text(((Component)this).transform, Tr.T("Building sounds"), 0f, 46f);
			obj2.fontSize = t.text.fontSize * 1.5f;
			obj2.alignment = (TextAlignmentOptions)514;
			((Graphic)obj2).color = Orange;
			RectTransform val6 = Row(((Component)this).transform, 40f, Tr.T("Turn off to play every building as in the normal game again."));
			Text((Transform)(object)val6, Tr.T("Enabled"), 280f, 40f);
			ToggleFor((Transform)(object)val6, BaseVolumeControlPlugin.ModEnabled);
			Spacer((Transform)(object)val6);
			val6 = Row(((Component)this).transform, 40f, Tr.T("Volume of all buildings at once, together with each building's own slider below."));
			Text((Transform)(object)val6, Tr.T("All buildings volume"), 280f, 40f);
			SliderFor((Transform)(object)val6, BaseVolumeControlPlugin.MasterVolume, 0, 100);
			Spacer((Transform)(object)val6);
			val6 = Row(((Component)this).transform, 44f, Tr.T("Press this key in game to silence every building at once, again to bring the sound back. Click the button to pick another key."));
			Text((Transform)(object)val6, Tr.T("Mute hotkey"), 280f, 44f);
			keyText = ((Component)ButtonFor((Transform)(object)val6, KeyName(), 260f, BeginKeyCapture)).GetComponentInChildren<TMP_Text>(true);
			refreshers.Add(delegate
			{
				keyText.text = KeyName();
			});
			Spacer((Transform)(object)val6);
			string text = Tr.T("Volume: how loud it is. 0% is silent, 100% is the normal game volume.") + "\n" + Tr.T("Range: how far away it can be heard. 100% is the normal distance, less means you only hear it up close.");
			val6 = Row(((Component)this).transform, 34f, text);
			Header((Transform)(object)val6, Tr.T("Building"), 280f);
			Header((Transform)(object)val6, Tr.T("Volume"), 316f);
			Header((Transform)(object)val6, Tr.T("Range"), 316f);
			Transform parent = ScrollList(((Component)this).transform);
			(Group, string)[] groups = Category.Groups;
			for (int num2 = 0; num2 < groups.Length; num2++)
			{
				(Group, string) tuple = groups[num2];
				Group group = tuple.Item1;
				string item = tuple.Item2;
				List<Category> list = (from c in Category.All
					where c.Group == @group
					orderby c == Category.Other
					select c).ToList();
				if (list.Count == 0)
				{
					continue;
				}
				TMP_Text obj3 = Text(parent, Tr.T(item), -1f, 34f);
				((Graphic)obj3).color = Orange;
				obj3.fontStyle = (FontStyles)1;
				foreach (Category item2 in list)
				{
					val6 = Row(parent, 38f, item2.Help + "\n" + text.Replace("\n", " "));
					Text((Transform)(object)val6, "   " + item2.Name, 280f, 38f);
					SliderFor((Transform)(object)val6, item2.VolumeEntry, 0, 100);
					SliderFor((Transform)(object)val6, item2.RangeEntry, 10, 100);
				}
			}
			helpDefault = Tr.T("Point at a row to see what it does.");
			help = Text(((Component)this).transform, helpDefault, -1f, 58f);
			help.textWrappingMode = (TextWrappingModes)1;
			help.overflowMode = (TextOverflowModes)0;
			help.alignment = (TextAlignmentOptions)257;
			help.fontSize = t.text.fontSize * 0.9f;
			((Graphic)help).color = HelpColor;
			status = Text(((Component)this).transform, "", -1f, 30f);
			status.alignment = (TextAlignmentOptions)514;
			((Graphic)status).color = Orange;
			val6 = Row(((Component)this).transform, 52f, Tr.T("Save keeps your changes and closes the window. Cancel (or Esc) puts everything back as it was when the window opened. Reset brings back the defaults."));
			Spacer((Transform)(object)val6);
			ButtonFor((Transform)(object)val6, Tr.T("Reset"), 200f, ResetAll);
			ButtonFor((Transform)(object)val6, Tr.T("Cancel"), 200f, Cancel);
			ButtonFor((Transform)(object)val6, Tr.T("Save"), 200f, Save);
			Spacer((Transform)(object)val6);
			opened = BaseVolumeControlPlugin.Snapshot();
			UpdateStatus();
		}

		internal void UpdateStatus()
		{
			status.text = (BaseVolumeControlPlugin.ChangedSince(opened) ? Tr.T("You have unsaved changes") : "");
		}

		internal void ShowHelp(string text)
		{
			help.text = (string.IsNullOrEmpty(text) ? helpDefault : text);
		}

		private void ResetAll()
		{
			BaseVolumeControlPlugin.ResetToDefaults();
			foreach (Action refresher in refreshers)
			{
				refresher();
			}
			UpdateStatus();
		}

		private void Update()
		{
			if (waitingForKey)
			{
				CaptureKey();
			}
			else if (Input.GetKeyDown((KeyCode)27) || ZInput.GetButtonDown("JoyButtonB"))
			{
				Cancel();
			}
			else
			{
				Wheel();
			}
		}

		private void Wheel()
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			RectTransform content = listScroll.content;
			float y = content.anchoredPosition.y;
			float y2 = Input.mouseScrollDelta.y;
			if (y2 != 0f && RectTransformUtility.RectangleContainsScreenPoint(listArea, Vector2.op_Implicit(Input.mousePosition), canvasCamera))
			{
				Rect rect = content.rect;
				float height = ((Rect)(ref rect)).height;
				rect = listScroll.viewport.rect;
				float num = Mathf.Max(0f, height - ((Rect)(ref rect)).height);
				if (wheelTarget < 0f)
				{
					wheelTarget = y;
				}
				wheelTarget = Mathf.Clamp(wheelTarget - y2 * 2f * 44f, 0f, num);
			}
			if (!(wheelTarget < 0f))
			{
				float num2 = Mathf.Lerp(y, wheelTarget, 1f - Mathf.Exp(-18f * Time.unscaledDeltaTime));
				if (Mathf.Abs(num2 - wheelTarget) < 0.5f)
				{
					num2 = wheelTarget;
					wheelTarget = -1f;
				}
				listScroll.StopMovement();
				content.anchoredPosition = new Vector2(content.anchoredPosition.x, num2);
			}
		}

		private void BeginKeyCapture()
		{
			waitingForKey = true;
			keyText.text = Tr.T("Press a key…");
		}

		private void CaptureKey()
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: 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_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Invalid comparison between Unknown and I4
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Invalid comparison between Unknown and I4
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			if (allKeys == null)
			{
				allKeys = (KeyCode[])Enum.GetValues(typeof(KeyCode));
			}
			if (!Input.GetKeyDown((KeyCode)27))
			{
				KeyCode val = (KeyCode)0;
				KeyCode[] array = allKeys;
				foreach (KeyCode val2 in array)
				{
					if ((int)val2 != 0 && ((int)val2 < 323 || (int)val2 > 329) && Input.GetKeyDown(val2))
					{
						val = val2;
						break;
					}
				}
				if ((int)val == 0)
				{
					return;
				}
				BaseVolumeControlPlugin.MuteKey.Value = new KeyboardShortcut(val, Array.Empty<KeyCode>());
				UpdateStatus();
			}
			waitingForKey = false;
			keyText.text = KeyName();
		}

		private static string KeyName()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			return ((object)BaseVolumeControlPlugin.MuteKey.Value/*cast due to .constrained prefix*/).ToString();
		}

		private RectTransform Row(Transform parent, float height, string helpText)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Expected O, but got Unknown
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Expected O, but got Unknown
			GameObject val = new GameObject("Row", new Type[1] { typeof(RectTransform) });
			val.transform.SetParent(parent, false);
			HorizontalLayoutGroup obj = val.AddComponent<HorizontalLayoutGroup>();
			((HorizontalOrVerticalLayoutGroup)obj).spacing = 16f;
			((LayoutGroup)obj).childAlignment = (TextAnchor)3;
			bool childControlWidth = (((HorizontalOrVerticalLayoutGroup)obj).childControlHeight = true);
			((HorizontalOrVerticalLayoutGroup)obj).childControlWidth = childControlWidth;
			childControlWidth = (((HorizontalOrVerticalLayoutGroup)obj).childForceExpandHeight = false);
			((HorizontalOrVerticalLayoutGroup)obj).childForceExpandWidth = childControlWidth;
			Size(val, -1f, height);
			Image val2 = val.AddComponent<Image>();
			((Graphic)val2).color = Color.clear;
			RowHover rowHover = val.AddComponent<RowHover>();
			rowHover.window = this;
			rowHover.text = helpText;
			rowHover.image = val2;
			return (RectTransform)val.transform;
		}

		private static void Spacer(Transform row)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("Spacer", new Type[1] { typeof(RectTransform) });
			val.transform.SetParent(row, false);
			val.AddComponent<LayoutElement>().flexibleWidth = 1f;
		}

		private static LayoutElement Size(GameObject go, float width, float height)
		{
			LayoutElement val = go.GetComponent<LayoutElement>() ?? go.AddComponent<LayoutElement>();
			if (width >= 0f)
			{
				float preferredWidth = (val.minWidth = width);
				val.preferredWidth = preferredWidth;
			}
			if (height >= 0f)
			{
				float preferredWidth = (val.minHeight = height);
				val.preferredHeight = preferredWidth;
			}
			return val;
		}

		private static T Clone<T>(T template, Transform parent) where T : Component
		{
			//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)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = Object.Instantiate<GameObject>(((Component)template).gameObject, parent, false);
			val.SetActive(true);
			FieldInfo fieldInfo = AccessTools.Field(typeof(UIGamePad), "m_hint");
			UIGamePad[] componentsInChildren = val.GetComponentsInChildren<UIGamePad>(true);
			foreach (UIGamePad val2 in componentsInChildren)
			{
				object? obj = fieldInfo?.GetValue(val2);
				GameObject val3 = (GameObject)((obj is GameObject) ? obj : null);
				if (val3 != null)
				{
					Object.DestroyImmediate((Object)(object)val3);
				}
				Object.Destroy((Object)(object)val2);
			}
			PlatformSpecificLocalization[] componentsInChildren2 = val.GetComponentsInChildren<PlatformSpecificLocalization>(true);
			for (int i = 0; i < componentsInChildren2.Length; i++)
			{
				Object.Destroy((Object)(object)componentsInChildren2[i]);
			}
			RectTransform val4 = (RectTransform)val.transform;
			Vector2 val5 = default(Vector2);
			((Vector2)(ref val5))..ctor(0.5f, 0.5f);
			val4.pivot = val5;
			Vector2 anchorMin = (val4.anchorMax = val5);
			val4.anchorMin = anchorMin;
			return val.GetComponent<T>();
		}

		private TMP_Text Text(Transform parent, string text, float width, float height)
		{
			TMP_Text obj = Clone<TMP_Text>(t.text, parent);
			obj.text = text;
			obj.alignment = (TextAlignmentOptions)4097;
			obj.textWrappingMode = (TextWrappingModes)0;
			obj.overflowMode = (TextOverflowModes)1;
			((Graphic)obj).raycastTarget = false;
			Size(((Component)obj).gameObject, width, height);
			return obj;
		}

		private void Header(Transform row, string text, float width)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			((Graphic)Text(row, text, width, 34f)).color = Orange;
		}

		private void SliderFor(Transform row, ConfigEntry<int> entry, int min, int max)
		{
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Expected O, but got Unknown
			Slider slider = Clone<Slider>(t.slider, row);
			TMP_Text[] componentsInChildren = ((Component)slider).GetComponentsInChildren<TMP_Text>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				Object.DestroyImmediate((Object)(object)((Component)componentsInChildren[i]).gameObject);
			}
			slider.onValueChanged = new SliderEvent();
			slider.minValue = min;
			slider.maxValue = max;
			slider.wholeNumbers = true;
			Size(((Component)slider).gameObject, 240f, 30f);
			TMP_Text value = Text(row, "", 60f, 30f);
			Show();
			refreshers.Add(Show);
			((UnityEvent<float>)(object)slider.onValueChanged).AddListener((UnityAction<float>)delegate(float v)
			{
				entry.Value = (int)v;
				value.text = entry.Value + "%";
				UpdateStatus();
			});
			void Show()
			{
				slider.SetValueWithoutNotify((float)entry.Value);
				value.text = entry.Value + "%";
			}
		}

		private void ToggleFor(Transform row, ConfigEntry<bool> entry)
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Expected O, but got Unknown
			if (!((Object)(object)t.toggle == (Object)null))
			{
				Toggle toggle = Clone<Toggle>(t.toggle, row);
				toggle.onValueChanged = new ToggleEvent();
				TMP_Text[] componentsInChildren = ((Component)toggle).GetComponentsInChildren<TMP_Text>(true);
				for (int i = 0; i < componentsInChildren.Length; i++)
				{
					((Component)componentsInChildren[i]).gameObject.SetActive(false);
				}
				Size(((Component)toggle).gameObject, 40f, 40f);
				toggle.SetIsOnWithoutNotify(entry.Value);
				refreshers.Add(delegate
				{
					toggle.SetIsOnWithoutNotify(entry.Value);
				});
				((UnityEvent<bool>)(object)toggle.onValueChanged).AddListener((UnityAction<bool>)delegate(bool v)
				{
					entry.Value = v;
					UpdateStatus();
				});
			}
		}

		private Button ButtonFor(Transform row, string text, float width, Action onClick)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Expected O, but got Unknown
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Expected O, but got Unknown
			Button obj = Clone<Button>(t.button, row);
			obj.onClick = new ButtonClickedEvent();
			((UnityEvent)obj.onClick).AddListener((UnityAction)delegate
			{
				onClick();
			});
			TMP_Text componentInChildren = ((Component)obj).GetComponentInChildren<TMP_Text>(true);
			if ((Object)(object)componentInChildren != (Object)null)
			{
				componentInChildren.text = text;
			}
			Size(((Component)obj).gameObject, width, 44f);
			return obj;
		}

		private Transform ScrollList(Transform parent)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Expected O, but got Unknown
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Expected O, but got Unknown
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Expected O, but got Unknown
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Expected O, but got Unknown
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			//IL_0198: Unknown result type (might be due to invalid IL or missing references)
			//IL_0199: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_022b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0235: Expected O, but got Unknown
			//IL_0244: Unknown result type (might be due to invalid IL or missing references)
			//IL_0249: Unknown result type (might be due to invalid IL or missing references)
			//IL_0254: 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_0269: Unknown result type (might be due to invalid IL or missing references)
			//IL_0273: Unknown result type (might be due to invalid IL or missing references)
			//IL_027e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0288: Unknown result type (might be due to invalid IL or missing references)
			//IL_0293: Unknown result type (might be due to invalid IL or missing references)
			//IL_029d: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("List", new Type[1] { typeof(RectTransform) });
			val.transform.SetParent(parent, false);
			Size(val, -1f, -1f).flexibleHeight = 1f;
			listArea = (RectTransform)val.transform;
			listScroll = val.AddComponent<ScrollRect>();
			listScroll.horizontal = false;
			listScroll.scrollSensitivity = 0f;
			listScroll.movementType = (MovementType)2;
			GameObject val2 = new GameObject("Viewport", new Type[1] { typeof(RectTransform) });
			val2.transform.SetParent(val.transform, false);
			RectTransform val3 = (RectTransform)val2.transform;
			val3.anchorMin = Vector2.zero;
			val3.anchorMax = Vector2.one;
			val3.offsetMin = Vector2.zero;
			val3.offsetMax = new Vector2((float)(((Object)(object)t.scrollbar != (Object)null) ? (-24) : 0), 0f);
			val2.AddComponent<RectMask2D>();
			GameObject val4 = new GameObject("Content", new Type[1] { typeof(RectTransform) });
			val4.transform.SetParent(val2.transform, false);
			RectTransform val5 = (RectTransform)val4.transform;
			val5.anchorMin = new Vector2(0f, 1f);
			val5.anchorMax = new Vector2(1f, 1f);
			val5.pivot = new Vector2(0.5f, 1f);
			Vector2 offsetMin = (val5.offsetMax = Vector2.zero);
			val5.offsetMin = offsetMin;
			VerticalLayoutGroup obj = val4.AddComponent<VerticalLayoutGroup>();
			((HorizontalOrVerticalLayoutGroup)obj).spacing = 6f;
			bool childControlWidth = (((HorizontalOrVerticalLayoutGroup)obj).childControlHeight = true);
			((HorizontalOrVerticalLayoutGroup)obj).childControlWidth = childControlWidth;
			((HorizontalOrVerticalLayoutGroup)obj).childForceExpandWidth = true;
			((HorizontalOrVerticalLayoutGroup)obj).childForceExpandHeight = false;
			val4.AddComponent<ContentSizeFitter>().verticalFit = (FitMode)2;
			listScroll.viewport = val3;
			listScroll.content = val5;
			if ((Object)(object)t.scrollbar != (Object)null)
			{
				Scrollbar val6 = Clone<Scrollbar>(t.scrollbar, val.transform);
				val6.onValueChanged = new ScrollEvent();
				val6.direction = (Direction)2;
				RectTransform val7 = (RectTransform)((Component)val6).transform;
				val7.anchorMin = new Vector2(1f, 0f);
				val7.anchorMax = new Vector2(1f, 1f);
				val7.pivot = new Vector2(1f, 0.5f);
				val7.sizeDelta = new Vector2(16f, 0f);
				val7.anchoredPosition = Vector2.zero;
				listScroll.verticalScrollbar = val6;
				listScroll.verticalScrollbarVisibility = (ScrollbarVisibility)1;
			}
			return val4.transform;
		}
	}
	internal static class Tr
	{
		private static readonly string[] languages = Translations.Languages;

		private static Dictionary<string, string[]> table;

		private static string cachedLanguage;

		private static int column = -1;

		public static string Language
		{
			get
			{
				try
				{
					return Localization.instance.GetSelectedLanguage();
				}
				catch
				{
					return PlatformPrefs.GetString("language", "English");
				}
			}
		}

		public static string T(string english)
		{
			string language = Language;
			if (language != cachedLanguage)
			{
				cachedLanguage = language;
				column = Array.IndexOf(languages, language);
			}
			if (column < 0)
			{
				return english;
			}
			if (table == null)
			{
				table = Translations.Build();
			}
			if (!table.TryGetValue(english, out var value) || column >= value.Length || string.IsNullOrEmpty(value[column]))
			{
				return english;
			}
			return value[column];
		}

		public static string GameText(string token, string english)
		{
			try
			{
				string text = Localization.instance.Localize("$" + token);
				if (!string.IsNullOrEmpty(text) && !text.StartsWith("[") && !text.StartsWith("$"))
				{
					return text;
				}
			}
			catch
			{
			}
			return T(english);
		}
	}
	internal static class Translations
	{
		public static readonly string[] Languages = new string[7] { "Russian", "Chinese", "German", "Spanish", "French", "Portuguese_Brazilian", "Polish" };

		public static Dictionary<string, string[]> Build()
		{
			Dictionary<string, string[]> dictionary = new Dictionary<string, string[]>();
			dictionary["Building sounds"] = new string[7] { "Звуки построек", "建筑音效", "Gebäudegeräusche", "Sonidos de construcciones", "Sons des constructions", "Sons das construções", "Dźwięki budowli" };
			dictionary["Enabled"] = new string[7] { "Включить", "启用", "Aktiviert", "Activado", "Activé", "Ativado", "Włączone" };
			dictionary["Turn off to play every building as in the normal game again."] = new string[7] { "Выключите, чтобы все постройки снова звучали как в обычной игре.", "关闭后,所有建筑的声音恢复为原版游戏的效果。", "Ausschalten, damit alle Gebäude wieder wie im normalen Spiel klingen.", "Desactívalo para que todas las construcciones vuelvan a sonar como en el juego normal.", "Désactivez pour que toutes les constructions sonnent de nouveau comme dans le jeu normal.", "Desative para que todas as construções voltem a soar como no jogo normal.", "Wyłącz, aby wszystkie budowle znów brzmiały jak w zwykłej grze." };
			dictionary["All buildings volume"] = new string[7] { "Громкость всех построек", "所有建筑音量", "Lautstärke aller Gebäude", "Volumen de todas las construcciones", "Volume de toutes les constructions", "Volume de todas as construções", "Głośność wszystkich budowli" };
			dictionary["Volume of all buildings at once, together with each building's own slider below."] = new string[7] { "Громкость всех построек сразу. Действует вместе с ползунком каждой постройки ниже.", "一次调整所有建筑的音量,与下方每种建筑各自的滑块共同生效。", "Lautstärke aller Gebäude auf einmal, zusammen mit dem eigenen Regler jedes Gebäudes darunter.", "Volumen de todas las construcciones a la vez, junto con el control propio de cada una más abajo.", "Volume de toutes les constructions à la fois, en plus du curseur propre à chacune ci-dessous.", "Volume de todas as construções de uma vez, junto com o controle próprio de cada uma abaixo.", "Głośność wszystkich budowli naraz, razem z własnym suwakiem każdej budowli poniżej." };
			dictionary["Mute hotkey"] = new string[7] { "Клавиша тишины", "静音热键", "Stumm-Taste", "Tecla de silencio", "Touche de sourdine", "Tecla de silenciar", "Klawisz wyciszenia" };
			dictionary["Press this key in game to silence every building at once, again to bring the sound back. Click the button to pick another key."] = new string[7] { "Нажмите эту клавишу в игре, чтобы сразу заглушить все постройки, ещё раз — чтобы вернуть звук. Щёлкните по кнопке, чтобы выбрать другую клавишу.", "在游戏中按下此键可立即让所有建筑静音,再按一次恢复声音。点击按钮可更换按键。", "Drücke diese Taste im Spiel, um alle Gebäude sofort stummzuschalten, erneut, um den Ton zurückzuholen. Klicke auf die Schaltfläche, um eine andere Taste zu wählen.", "Pulsa esta tecla en el juego para silenciar todas las construcciones a la vez y otra vez para recuperar el sonido. Haz clic en el botón para elegir otra tecla.", "Appuyez sur cette touche en jeu pour couper toutes les constructions d'un coup, et encore pour rétablir le son. Cliquez sur le bouton pour choisir une autre touche.", "Pressione esta tecla no jogo para silenciar todas as construções de uma vez e de novo para trazer o som de volta. Clique no botão para escolher outra tecla.", "Naciśnij ten klawisz w grze, aby od razu wyciszyć wszystkie budowle, a ponownie, aby przywrócić dźwięk. Kliknij przycisk, aby wybrać inny klawisz." };
			dictionary["Press a key…"] = new string[7] { "Нажмите клавишу…", "请按一个键…", "Taste drücken…", "Pulsa una tecla…", "Appuyez sur une touche…", "Pressione uma tecla…", "Naciśnij klawisz…" };
			dictionary["Building"] = new string[7] { "Постройка", "建筑", "Gebäude", "Construcción", "Construction", "Construção", "Budowla" };
			dictionary["Volume"] = new string[7] { "Громкость", "音量", "Lautstärke", "Volumen", "Volume", "Volume", "Głośność" };
			dictionary["Range"] = new string[7] { "Дальность", "距离", "Reichweite", "Alcance", "Portée", "Alcance", "Zasięg" };
			dictionary["Volume: how loud it is. 0% is silent, 100% is the normal game volume."] = new string[7] { "Громкость: насколько громко звучит. 0% — тишина, 100% — как в обычной игре.", "音量:声音有多大。0% 为静音,100% 为游戏原本的音量。", "Lautstärke: wie laut es ist. 0 % ist stumm, 100 % ist die normale Lautstärke des Spiels.", "Volumen: lo fuerte que suena. 0 % es silencio, 100 % es el volumen normal del juego.", "Volume : à quel point c'est fort. 0 % est muet, 100 % est le volume normal du jeu.", "Volume: o quão alto soa. 0% é silêncio, 100% é o volume normal do jogo.", "Głośność: jak głośno brzmi. 0% to cisza, 100% to zwykła głośność gry." };
			dictionary["Range: how far away it can be heard. 100% is the normal distance, less means you only hear it up close."] = new string[7] { "Дальность: с какого расстояния слышно. 100% — как в обычной игре, меньше — слышно только вблизи.", "距离:在多远的地方能听到。100% 为原本的距离,数值越小,只有靠近时才能听到。", "Reichweite: aus welcher Entfernung man es hört. 100 % ist die normale Entfernung, weniger heißt, man hört es nur aus der Nähe.", "Alcance: desde qué distancia se oye. 100 % es la distancia normal; menos significa que solo se oye de cerca.", "Portée : de quelle distance on l'entend. 100 % est la distance normale, moins signifie qu'on ne l'entend que de près.", "Alcance: de que distância dá para ouvir. 100% é a distância normal; menos significa que só se ouve de perto.", "Zasięg: z jakiej odległości słychać. 100% to zwykła odległość, mniej oznacza, że słychać tylko z bliska." };
			dictionary["Point at a row to see what it does."] = new string[7] { "Наведите мышь на строку, чтобы увидеть, за что она отвечает.", "将鼠标指向一行,即可查看它的作用。", "Zeige auf eine Zeile, um zu sehen, wofür sie ist.", "Pasa el ratón por una fila para ver para qué sirve.", "Survolez une ligne pour voir à quoi elle sert.", "Passe o mouse sobre uma linha para ver para que ela serve.", "Najedź myszą na wiersz, aby zobaczyć, do czego służy." };
			dictionary["Save keeps your changes and closes the window. Cancel (or Esc) puts everything back as it was when the window opened. Reset brings back the defaults."] = new string[7] { "Сохранить — запомнить изменения и закрыть окно. Отмена (или Esc) — вернуть всё как было при открытии окна. Сбросить — вернуть значения по умолчанию.", "保存:保留更改并关闭窗口。取消(或 Esc):恢复到打开窗口时的状态。重置:恢复默认值。", "Speichern behält die Änderungen und schließt das Fenster. Abbrechen (oder Esc) stellt alles wieder her, wie es beim Öffnen war. Zurücksetzen stellt die Standardwerte wieder her.", "Guardar conserva los cambios y cierra la ventana. Cancelar (o Esc) deja todo como estaba al abrirla. Restablecer vuelve a los valores predeterminados.", "Enregistrer conserve vos modifications et ferme la fenêtre. Annuler (ou Échap) remet tout comme à l'ouverture. Réinitialiser rétablit les valeurs par défaut.", "Salvar mantém as alterações e fecha a janela. Cancelar (ou Esc) deixa tudo como estava ao abrir a janela. Redefinir volta aos valores padrão.", "Zapisz zachowuje zmiany i zamyka okno. Anuluj (lub Esc) przywraca wszystko jak przy otwarciu okna. Resetuj przywraca wartości domyślne." };
			dictionary["Reset"] = new string[7] { "Сбросить", "重置", "Zurücksetzen", "Restablecer", "Réinitialiser", "Redefinir", "Resetuj" };
			dictionary["Cancel"] = new string[7] { "Отмена", "取消", "Abbrechen", "Cancelar", "Annuler", "Cancelar", "Anuluj" };
			dictionary["Save"] = new string[7] { "Сохранить", "保存", "Speichern", "Guardar", "Enregistrer", "Salvar", "Zapisz" };
			dictionary["You have unsaved changes"] = new string[7] { "Есть несохранённые изменения", "有未保存的更改", "Es gibt ungespeicherte Änderungen", "Hay cambios sin guardar", "Modifications non enregistrées", "Há alterações não salvas", "Masz niezapisane zmiany" };
			dictionary["Building sounds saved"] = new string[7] { "Звуки построек сохранены", "建筑音效已保存", "Gebäudegeräusche gespeichert", "Sonidos de construcciones guardados", "Sons des constructions enregistrés", "Sons das construções salvos", "Dźwięki budowli zapisane" };
			dictionary["Building sounds muted"] = new string[7] { "Звуки построек выключены", "建筑音效已静音", "Gebäudegeräusche stumm", "Sonidos de construcciones silenciados", "Sons des constructions coupés", "Sons das construções silenciados", "Dźwięki budowli wyciszone" };
			dictionary["Building sounds back"] = new string[7] { "Звуки построек включены", "建筑音效已恢复", "Gebäudegeräusche wieder an", "Sonidos de construcciones restaurados", "Sons des constructions rétablis", "Sons das construções de volta", "Dźwięki budowli przywrócone" };
			dictionary["Protection"] = new string[7] { "Защита", "防护", "Schutz", "Protección", "Protection", "Proteção", "Ochrona" };
			dictionary["Travel"] = new string[7] { "Перемещение", "出行", "Reisen", "Viajes", "Voyage", "Viagem", "Podróże" };
			dictionary["Production"] = new string[7] { "Производство", "生产", "Herstellung", "Producción", "Production", "Produção", "Produkcja" };
			dictionary["Farm and kitchen"] = new string[7] { "Хозяйство", "农场与厨房", "Hof und Küche", "Granja y cocina", "Ferme et cuisine", "Fazenda e cozinha", "Gospodarstwo i kuchnia" };
			dictionary["Fire and light"] = new string[7] { "Огонь и свет", "火与光", "Feuer und Licht", "Fuego y luz", "Feu et lumière", "Fogo e luz", "Ogień i światło" };
			dictionary["Everything else"] = new string[7] { "Прочее", "其他", "Alles andere", "Todo lo demás", "Tout le reste", "Todo o resto", "Wszystko inne" };
			dictionary["Shield generator hum"] = new string[7] { "Гул генератора щита", "盾牌生成器嗡鸣", "Summen des Schildgenerators", "Zumbido del generador de escudo", "Bourdonnement du générateur de bouclier", "Zumbido do gerador de escudo", "Buczenie generatora tarczy" };
			dictionary["Shield generator hits"] = new string[7] { "Удары по щиту", "盾牌受击", "Treffer auf den Schild", "Impactos en el escudo", "Coups sur le bouclier", "Impactos no escudo", "Trafienia w tarczę" };
			dictionary["Portals"] = new string[7] { "Порталы", "传送门", "Portale", "Portales", "Portails", "Portais", "Portale" };
			dictionary["Beehives and nests"] = new string[7] { "Ульи и гнёзда", "蜂房与鸟巢", "Bienenstöcke und Nester", "Colmenas y nidos", "Ruches et nids", "Colmeias e ninhos", "Ule i gniazda" };
			dictionary["Fermenters"] = new string[7] { "Бродильные бочки", "发酵桶", "Fermentierer", "Fermentadores", "Fermenteurs", "Fermentadores", "Kadzie fermentacyjne" };
			dictionary["Sap extractors"] = new string[7] { "Экстракторы живицы", "树汁提取器", "Harzextraktoren", "Extractores de savia", "Extracteurs de sève", "Coletores de seiva", "Odsysacze soków" };
			dictionary["Obliterators"] = new string[7] { "Мусоросжигатели", "湮灭者", "Vernichter", "Obliteradores", "Oblitérateurs", "Obliteradores", "Unicestwiacze" };
			dictionary["Ships"] = new string[7] { "Корабли", "船只", "Schiffe", "Barcos", "Navires", "Navios", "Statki" };
			dictionary["Fires and torches"] = new string[7] { "Костры и факелы", "篝火与火把", "Feuer und Fackeln", "Hogueras y antorchas", "Feux et torches", "Fogueiras e tochas", "Ogniska i pochodnie" };
			dictionary["Cooking stations"] = new string[7] { "Готовка", "烹饪台", "Kochstellen", "Estaciones de cocina", "Postes de cuisine", "Estações de cozinha", "Stanowiska kuchenne" };
			dictionary["Other buildings"] = new string[7] { "Остальные постройки", "其他建筑", "Andere Gebäude", "Otras construcciones", "Autres constructions", "Outras construções", "Inne budowle" };
			dictionary["Forges and tables"] = new string[7] { "Кузницы и столы", "锻炉与工作台", "Schmieden und Tische", "Forjas y mesas", "Forges et tables", "Forjas e mesas", "Kuźnie i stoły" };
			dictionary["Frost foundry and kiln"] = new string[7] { "Морозные литейня и печь", "冰霜铸造厂与窑炉", "Frostgießerei und Frostofen", "Fundición y horno de escarcha", "Fonderie et four de givre", "Fundição e forno de gelo", "Mroźna odlewnia i piec" };
			dictionary["Wards"] = new string[7] { "Обереги", "守护石", "Schutzsteine", "Guardas", "Protections", "Guardiões", "Strażnice" };
			dictionary["Cauldrons"] = new string[7] { "Котлы", "铸铁锅", "Kessel", "Calderos", "Chaudrons", "Caldeirões", "Kociołki" };
			dictionary["Carts and sleds"] = new string[7] { "Тележки и сани", "推车与雪橇", "Karren und Schlitten", "Carros y trineos", "Charrettes et traîneaux", "Carroças e trenós", "Wózki i sanie" };
			dictionary["Siege engines"] = new string[7] { "Осадные орудия", "攻城器械", "Belagerungsmaschinen", "Máquinas de asedio", "Engins de siège", "Máquinas de cerco", "Machiny oblężnicze" };
			dictionary["Smelter"] = new string[7] { "Плавильня", "冶炼炉", "Schmelzofen", "Horno de fundición", "Fonderie", "Fundição", "Odlewnia" };
			dictionary["Blast furnace"] = new string[7] { "Доменная печь", "高炉", "Hochofen", "Gran horno", "Haut fourneau", "Alto-forno", "Piec hutniczy" };
			dictionary["Charcoal kiln"] = new string[7] { "Углевыжигательная печь", "木炭窑", "Kohlenmeiler", "Horno de carbón", "Four à charbon", "Forno de carvão", "Mielerz" };
			dictionary["Eitr refinery"] = new string[7] { "Машина для переработки эйтра", "埃达之力提炼器", "Eitr-Raffinerie", "Refinería de Eitr", "Raffinerie d'Eitr", "Refinaria de éter", "Rafineria Eitru" };
			dictionary["Spinning wheel"] = new string[7] { "Прялка", "纺车", "Spinnrad", "Rueca", "Roue", "Roda de fiar", "Kołowrotek" };
			dictionary["Windmill"] = new string[7] { "Мельница", "风车", "Windmühle", "Molino de viento", "Moulin", "Moinho", "Wiatrak" };
			dictionary["Hot tub"] = new string[7] { "Ванна", "浴缸", "Badezuber", "Bañera", "Baignoire", "Banheira", "Wanna" };
			dictionary["The constant hum of the shield generator and its dome, its start and stop, and the low fuel warning."] = new string[7] { "Постоянный гул генератора щита и купола, звук включения и выключения, сигнал о нехватке топлива.", "盾牌生成器及其护罩持续的嗡鸣声、开启与关闭声,以及燃料不足的警告声。", "Das ständige Summen des Schildgenerators und seiner Kuppel, sein Ein- und Ausschalten und die Warnung bei wenig Brennstoff.", "El zumbido constante del generador de escudo y su cúpula, su encendido y apagado, y el aviso de poco combustible.", "Le bourdonnement constant du générateur de bouclier et de son dôme, son allumage et son arrêt, et l'alerte de carburant bas.", "O zumbido constante do gerador de escudo e da cúpula, o liga e desliga e o aviso de pouco combustível.", "Ciągłe buczenie generatora tarczy i kopuły, jego włączanie i wyłączanie oraz ostrzeżenie o małej ilości paliwa." };
			dictionary["The sound of enemies and projectiles hitting the dome."] = new string[7] { "Звук, когда враги или снаряды попадают по куполу.", "敌人或投射物击中护罩时的声音。", "Das Geräusch, wenn Gegner oder Geschosse die Kuppel treffen.", "El sonido de enemigos y proyectiles golpeando la cúpula.", "Le bruit des ennemis et des projectiles qui frappent le dôme.", "O som de inimigos e projéteis atingindo a cúpula.", "Dźwięk wrogów i pocisków trafiających w kopułę." };
			dictionary["The hum of portals."] = new string[7] { "Гул порталов.", "传送门的嗡鸣声。", "Das Summen der Portale.", "El zumbido de los portales.", "Le bourdonnement des portails.", "O zumbido dos portais.", "Buczenie portali." };
			dictionary["The roar of the smelter and the sound of each finished bar."] = new string[7] { "Гул плавильни и звук каждого готового слитка.", "冶炼炉的轰鸣声和每根金属锭完成时的声音。", "Das Fauchen des Schmelzofens und der Klang jedes fertigen Barrens.", "El rugido del horno de fundición y el sonido de cada lingote terminado.", "Le grondement de la fonderie et le son de chaque lingot terminé.", "O ronco da fundição e o som de cada barra pronta.", "Huk odlewni i dźwięk każdej gotowej sztabki." };
			dictionary["The roar of the blast furnace and the sound of each finished bar."] = new string[7] { "Гул доменной печи и звук каждого готового слитка.", "高炉的轰鸣声和每根金属锭完成时的声音。", "Das Fauchen des Hochofens und der Klang jedes fertigen Barrens.", "El rugido del gran horno y el sonido de cada lingote terminado.", "Le grondement du haut fourneau et le son de chaque lingot terminé.", "O ronco do alto-forno e o som de cada barra pronta.", "Huk pieca hutniczego i dźwięk każdej gotowej sztabki." };
			dictionary["The fire of the charcoal kiln and the sound of each piece of coal."] = new string[7] { "Огонь углевыжигательной печи и звук каждого готового угля.", "木炭窑的火焰声和每块木炭完成时的声音。", "Das Feuer des Kohlenmeilers und der Klang jedes fertigen Stücks Kohle.", "El fuego del horno de carbón y el sonido de cada carbón terminado.", "Le feu du four à charbon et le son de chaque morceau de charbon.", "O fogo do forno de carvão e o som de cada carvão pronto.", "Ogień mielerza i dźwięk każdego gotowego węgla." };
			dictionary["The eitr refinery at work and the sound of each finished eitr."] = new string[7] { "Работа машины для переработки эйтра и звук каждого готового эйтра.", "埃达之力提炼器运作时的声音和每份成品完成时的声音。", "Die Eitr-Raffinerie bei der Arbeit und der Klang jedes fertigen Eitr.", "La refinería de Eitr funcionando y el sonido de cada Eitr terminado.", "La raffinerie d'Eitr en marche et le son de chaque Eitr terminé.", "A refinaria de éter funcionando e o som de cada éter pronto.", "Praca rafinerii Eitru i dźwięk każdego gotowego Eitru." };
			dictionary["The spinning wheel turning and the sound of each finished thread."] = new string[7] { "Стук прялки и звук каждой готовой нити.", "纺车转动的声音和每根线完成时的声音。", "Das sich drehende Spinnrad und der Klang jedes fertigen Fadens.", "La rueca girando y el sonido de cada hilo terminado.", "La roue qui tourne et le son de chaque fil terminé.", "A roda de fiar girando e o som de cada fio pronto.", "Obracający się kołowrotek i dźwięk każdej gotowej nici." };
			dictionary["The creaking sails of the windmill and the sound of each finished flour."] = new string[7] { "Скрип лопастей мельницы и звук каждой готовой муки.", "风车叶片的吱呀声和每份面粉完成时的声音。", "Die knarrenden Flügel der Windmühle und der Klang jedes fertigen Mehls.", "Las aspas crujientes del molino y el sonido de cada harina terminada.", "Les ailes grinçantes du moulin et le son de chaque farine terminée.", "As pás rangendo do moinho e o som de cada farinha pronta.", "Skrzypiące skrzydła wiatraka i dźwięk każdej gotowej mąki." };
			dictionary["The buzzing of bees in beehives and the birds of bird nests."] = new string[7] { "Жужжание пчёл в ульях и птицы в гнёздах.", "蜂房里蜜蜂的嗡嗡声和鸟巢里的鸟叫声。", "Das Summen der Bienen in Bienenstöcken und die Vögel in Vogelnestern.", "El zumbido de las abejas en las colmenas y los pájaros de los nidos.", "Le bourdonnement des abeilles dans les ruches et les oiseaux des nids.", "O zumbido das abelhas nas colmeias e os pássaros dos ninhos.", "Brzęczenie pszczół w ulach i ptaki w gniazdach." };
			dictionary["The bubbling of fermenters."] = new string[7] { "Бульканье бродильных бочек.", "发酵桶的咕嘟声。", "Das Blubbern der Fermentierer.", "El burbujeo de los fermentadores.", "Le bouillonnement des fermenteurs.", "O borbulhar dos fermentadores.", "Bulgotanie kadzi fermentacyjnych." };
			dictionary["Sap extractors on Yggdrasil roots."] = new string[7] { "Экстракторы живицы на корнях Иггдрасиля.", "世界树根上的树汁提取器。", "Harzextraktoren an Yggdrasil-Wurzeln.", "Extractores de savia en raíces de Yggdrasil.", "Extracteurs de sève sur les racines d'Yggdrasil.", "Coletores de seiva nas raízes de Yggdrasil.", "Odsysacze soków na korzeniach Yggdrasila." };
			dictionary["The obliterator and its lightning."] = new string[7] { "Мусоросжигатель и его молнии.", "湮灭者及其闪电。", "Der Vernichter und seine Blitze.", "El obliterador y sus rayos.", "L'oblitérateur et ses éclairs.", "O obliterador e seus raios.", "Unicestwiacz i jego błyskawice." };
			dictionary["Creaking hulls, sails and water around ships, also while sailing."] = new string[7] { "Скрип корпуса, паруса и вода у кораблей, в том числе в плавании.", "船体吱呀声、船帆与船周围的水声,航行时同样有效。", "Knarrende Rümpfe, Segel und Wasser um Schiffe, auch während der Fahrt.", "Cascos que crujen, velas y agua alrededor de los barcos, también al navegar.", "Coques qui grincent, voiles et eau autour des navires, même en navigation.", "Cascos rangendo, velas e água ao redor dos navios, também navegando.", "Skrzypiące kadłuby, żagle i woda wokół statków, także podczas żeglugi." };
			dictionary["The crackle of campfires, hearths, braziers, torches and sconces."] = new string[7] { "Треск костров, очагов, жаровен, факелов и настенных светильников.", "篝火、火炉、火盆、火把和壁灯的噼啪声。", "Das Knistern von Lagerfeuern, Feuerstellen, Feuerschalen, Fackeln und Wandleuchten.", "El crepitar de hogueras, chimeneas, braseros, antorchas y apliques.", "Le crépitement des feux de camp, foyers, braseros, torches et appliques.", "O crepitar de fogueiras, lareiras, braseiros, tochas e arandelas.", "Trzask ognisk, palenisk, koksowników, pochodni i kinkietów." };
			dictionary["Food sizzling on cooking stations and in the oven."] = new string[7] { "Шипение еды на вертелах, сковородах и в печи.", "烹饪台和烤炉上食物的滋滋声。", "Brutzelndes Essen auf Kochstellen und im Ofen.", "La comida chisporroteando en las estaciones de cocina y en el horno.", "La nourriture qui grésille sur les postes de cuisine et dans le four.", "Comida chiando nas estações de cozinha e no forno.", "Skwierczące jedzenie na stanowiskach kuchennych i w piecu." };
			dictionary["Constant sounds of any other building that is not in the list above."] = new string[7] { "Постоянные звуки любых других построек, которых нет в списке выше.", "上方列表中未列出的其他建筑的持续声音。", "Dauerhafte Geräusche aller anderen Gebäude, die oben nicht aufgeführt sind.", "Sonidos constantes de cualquier otra construcción que no esté en la lista de arriba.", "Les sons constants de toute autre construction absente de la liste ci-dessus.", "Sons constantes de qualquer outra construção que não esteja na lista acima.", "Stałe dźwięki wszystkich innych budowli, których nie ma na liście powyżej." };
			dictionary["The hum and fire of the forge, the black forge and the galdr table."] = new string[7] { "Гул и огонь кузницы, чёрной кузницы и стола гальдра.", "锻炉、黑金属锻炉和咒术台的嗡鸣与火焰声。", "Summen und Feuer der Schmiede, der Schwarzschmiede und des Galdrtischs.", "El zumbido y el fuego de la forja, la forja negra y la mesa de galdr.", "Le bourdonnement et le feu de la forge, de la forge noire et de la table de galdr.", "O zumbido e o fogo da forja, da forja negra e da mesa de galdr.", "Buczenie i ogień kuźni, czarnej kuźni i stołu galdr." };
			dictionary["The frost foundry and the frost kiln at work."] = new string[7] { "Работа морозной литейни и морозной печи.", "冰霜铸造厂和冰霜窑炉运作时的声音。", "Frostgießerei und Frostofen bei der Arbeit.", "La fundición y el horno de escarcha funcionando.", "La fonderie et le four de givre en marche.", "A fundição e o forno de gelo funcionando.", "Praca mroźnej odlewni i mroźnego pieca." };
			dictionary["The hum of an active ward."] = new string[7] { "Гул включённого оберега.", "激活的守护石的嗡鸣声。", "Das Summen eines aktiven Schutzsteins.", "El zumbido de una guarda activa.", "Le bourdonnement d'une protection active.", "O zumbido de um guardião ativo.", "Buczenie aktywnej strażnicy." };
			dictionary["Bubbling cauldrons and mead cauldrons over the fire."] = new string[7] { "Бульканье котлов и котлов для медовухи над огнём.", "火上铸铁锅和蜜酒锅的咕嘟声。", "Blubbernde Kessel und Metkessel über dem Feuer.", "Calderos y calderos de hidromiel burbujeando sobre el fuego.", "Chaudrons et chaudrons à hydromel qui bouillonnent sur le feu.", "Caldeirões e caldeirões de hidromel borbulhando sobre o fogo.", "Bulgoczące kociołki i kociołki do miodu pitnego nad ogniem." };
			dictionary["The water and the fire of the hot tub."] = new string[7] { "Вода и огонь ванны.", "浴缸的水声与火声。", "Wasser und Feuer des Badezubers.", "El agua y el fuego de la bañera.", "L'eau et le feu de la baignoire.", "A água e o fogo da banheira.", "Woda i ogień wanny." };
			dictionary["Wheels and runners of carts and sleds."] = new string[7] { "Колёса и полозья тележек и саней.", "推车车轮与雪橇滑板的声音。", "Räder und Kufen von Karren und Schlitten.", "Ruedas y patines de carros y trineos.", "Roues et patins des charrettes et traîneaux.", "Rodas e esquis de carroças e trenós.", "Koła i płozy wózków i sań." };
			dictionary["The battering ram and the catapult."] = new string[7] { "Таран и катапульта.", "攻城锤和投石机。", "Rammbock und Katapult.", "El ariete y la catapulta.", "Le bélier et la catapulte.", "O aríete e a catapulta.", "Taran i katapulta." };
			return dictionary;
		}
	}
}