Decompiled source of RunCustomizer v1.0.0

Mods/RunCustomizer.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using HarmonyLib;
using Il2Cpp;
using Il2CppInterop.Common;
using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSystem;
using Il2CppSystem.Collections.Generic;
using Il2CppSystem.Reflection;
using Il2CppTMPro;
using MelonLoader;
using MelonLoader.Preferences;
using Microsoft.CodeAnalysis;
using RunCustomizer;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Localization;
using UnityEngine.Localization.Components;
using UnityEngine.Localization.Settings;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(RunCustomizerMod), "Run Customizer", "1.0.0", "Relsev", null)]
[assembly: MelonGame("BoltBlasterGames", "TheSpellBrigade")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("RunCustomizer")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("RunCustomizer")]
[assembly: AssemblyTitle("RunCustomizer")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.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 RunCustomizer
{
	internal static class DifficultyHooks
	{
		private static bool _readingBase;

		private static float[] _baseGold;

		private static int Base => Math.Clamp((int)Modes.RunDifficulty, 0, 2);

		public static void EnemyMultiplierPostfix(ref float __result)
		{
			if (Modes.InRun)
			{
				__result = Modes.Active.Enemy;
			}
		}

		public static void CycleMultiplierPostfix(ref float __result)
		{
			if (Modes.InRun)
			{
				__result *= Modes.Active.Enemy / Modes.BaseEnemy[Base];
			}
		}

		public static void SpawnIntervalPostfix(ref float __result)
		{
			if (Modes.InRun)
			{
				__result *= Modes.BaseSpawnSpeed[Base] / Modes.Active.SpawnSpeed;
			}
		}

		public static void MinEnemiesPostfix(ref int __result)
		{
			if (Modes.InRun)
			{
				__result = Math.Max(1, (int)Math.Round((float)__result * Modes.Active.EnemyCount / Modes.BaseEnemyCount[Base]));
			}
		}

		public static bool DeterminePickUpPrefix(PickupSpawner __instance, float pickupChanceMultiplier, float spawningPlayerHealthPercentage, ref NetworkObject __result)
		{
			if (!Modes.InRun)
			{
				return true;
			}
			try
			{
				float value = Random.value;
				float num = 0f;
				if (HealthPickup.AmountOfHealthPickupsOnMap < __instance.maxAmountOfHealthPickupsOnMap)
				{
					num = __instance.healthPickupChanceCurve.Evaluate(spawningPlayerHealthPercentage) * Modes.Active.HealthDrops * pickupChanceMultiplier;
				}
				if (num > value)
				{
					__result = __instance.healthPickupPrefab;
				}
				else if (pickupChanceMultiplier * 0.0016f + num > value && __instance.EnoughXPOrbsToAttract())
				{
					__result = __instance.magnetPickupPrefab;
				}
				else
				{
					__result = null;
				}
				return false;
			}
			catch (Exception ex)
			{
				RunCustomizerMod.Log.Error("pickup roll failed, using the game's: " + ex.Message);
				return true;
			}
		}

		public static void GoldMultiplierPostfix(RunGoldCalculatorData __instance, Difficulty difficulty, ref float __result)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			if (!Modes.InRun || _readingBase || difficulty != Modes.RunDifficulty)
			{
				return;
			}
			try
			{
				if (_baseGold == null)
				{
					_readingBase = true;
					_baseGold = new float[3];
					for (int i = 0; i < 3; i++)
					{
						_baseGold[i] = __instance.GetGoldMultiplierForDifficulty((Difficulty)i);
					}
				}
				float num = Math.Max(0f, Interpolate(_baseGold, Modes.Score(Modes.Active)));
				bool flag = Math.Max(_baseGold[1], _baseGold[2]) <= 5f;
				__result = (flag ? ((float)Math.Round(num, 2)) : ((float)Math.Round(num)));
			}
			catch (Exception ex)
			{
				RunCustomizerMod.Log.Error("gold bonus: " + ex.Message);
			}
			finally
			{
				_readingBase = false;
			}
		}

		public static float Interpolate(float[] points, float position)
		{
			int num = ((!(position < 1f)) ? 1 : 0);
			return points[num] + (points[num + 1] - points[num]) * (position - (float)num);
		}
	}
	internal static class DifficultyUi
	{
		private sealed class Row
		{
			public Mode Owner;

			public Selector Selector;

			public Func<List<string>> Options;

			public Func<string> Current;

			public Action<string> Apply;
		}

		private const string ButtonPrefix = "RC_Mode_";

		private const string RowsName = "RC_Rows";

		private const float RowHeight = 80f;

		private static readonly Mode[] Buttons = new Mode[2]
		{
			Mode.Random,
			Mode.Custom
		};

		public static readonly Dictionary<Mode, Color> NameColors = new Dictionary<Mode, Color>
		{
			[Mode.Random] = new Color(0.84f, 0.48f, 1f),
			[Mode.Custom] = new Color(0.37f, 0.88f, 0.94f),
			[Mode.RandomBase] = new Color(0.84f, 0.48f, 1f)
		};

		private static readonly HashSet<IntPtr> SubscribedBaseButtons = new HashSet<IntPtr>();

		private static bool _clickingForMode;

		private static readonly List<Row> Rows = new List<Row>();

		private static Mode RandomMode
		{
			get
			{
				if (!RunCustomizerMod.RandomBaseOnly.Value)
				{
					return Mode.Random;
				}
				return Mode.RandomBase;
			}
		}

		public static Sprite Icon(Mode mode)
		{
			return Sprites.Get(mode switch
			{
				Mode.Random => "random.png", 
				Mode.Custom => "custom.png", 
				_ => "randombase.png", 
			});
		}

		private static Mode ButtonOf(Mode mode)
		{
			if (mode != Mode.RandomBase)
			{
				return mode;
			}
			return Mode.Random;
		}

		public static void InitializeButtonsPostfix(DifficultiesPanel __instance)
		{
			try
			{
				Build(__instance);
			}
			catch (Exception value)
			{
				RunCustomizerMod.Log.Error($"[difficulty ui] {value}");
			}
		}

		public static void ShowPostfix(DifficultiesPanel __instance)
		{
			try
			{
				RefreshSelection(__instance);
				if (Modes.Selected != Mode.Vanilla)
				{
					ShowModeDetails(__instance, ButtonOf(Modes.Selected));
				}
			}
			catch (Exception value)
			{
				RunCustomizerMod.Log.Error($"[difficulty ui] {value}");
			}
		}

		public static void ShowDifficultyDetailsPostfix(DifficultiesPanel __instance)
		{
			try
			{
				foreach (LocalizeStringEvent item in Localizers(__instance))
				{
					if (!((Object)(object)item == (Object)null) && !((Behaviour)item).enabled)
					{
						((Behaviour)item).enabled = true;
						item.RefreshString();
					}
				}
				ShowRows(__instance, null);
			}
			catch (Exception value)
			{
				RunCustomizerMod.Log.Error($"[difficulty ui] {value}");
			}
		}

		private static void Build(DifficultiesPanel panel)
		{
			List<DifficultySelectionButton> difficultyButtons = panel.difficultyButtons;
			if (difficultyButtons == null || difficultyButtons.Count == 0)
			{
				return;
			}
			DifficultySelectionButton val = null;
			Enumerator<DifficultySelectionButton> enumerator = difficultyButtons.GetEnumerator();
			while (enumerator.MoveNext())
			{
				DifficultySelectionButton current = enumerator.Current;
				if ((Object)(object)current == (Object)null || ((Object)current).name.StartsWith("RC_Mode_"))
				{
					continue;
				}
				if (val == null)
				{
					val = current;
				}
				if (!SubscribedBaseButtons.Add(((Il2CppObjectBase)current).Pointer))
				{
					continue;
				}
				((ButtonInteractionHook)current).OnClicked += Action.op_Implicit((Action)delegate
				{
					if (!_clickingForMode)
					{
						Modes.Selected = Mode.Vanilla;
						RefreshSelection(panel);
						RefreshLobbyIcons();
					}
				});
			}
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			Transform parent = ((Component)val).transform.parent;
			if ((Object)(object)parent.Find("RC_Mode_" + Mode.Random) != (Object)null)
			{
				return;
			}
			Mode[] buttons = Buttons;
			for (int num = 0; num < buttons.Length; num++)
			{
				Mode mode = buttons[num];
				GameObject obj = Object.Instantiate<GameObject>(((Component)val).gameObject, parent);
				((Object)obj).name = "RC_Mode_" + mode;
				obj.transform.SetAsLastSibling();
				DifficultySelectionButton component = obj.GetComponent<DifficultySelectionButton>();
				component._Difficulty_k__BackingField = (Difficulty)(100 + mode);
				MuteSerializedClicks(obj.GetComponent<Button>());
				Sprite sprite = Icon((mode == Mode.Random) ? RandomMode : mode);
				if ((Object)(object)component.iconImage != (Object)null)
				{
					component.iconImage.sprite = sprite;
				}
				if ((Object)(object)component.iconImageLocked != (Object)null)
				{
					component.iconImageLocked.sprite = sprite;
				}
				Mode m = mode;
				((ButtonInteractionHook)component).OnClicked += Action.op_Implicit((Action)delegate
				{
					Select(panel, (m == Mode.Random) ? RandomMode : m);
				});
				((ButtonInteractionHook)component).OnHighlight += Action.op_Implicit((Action)delegate
				{
					ShowModeDetails(panel, m);
				});
			}
			BuildRows(panel);
			RefreshSelection(panel);
		}

		private static void Select(DifficultiesPanel panel, Mode mode)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Modes.Selected = mode;
				Modes.Reroll();
				Difficulty item = Modes.Pending.Base;
				Enumerator<DifficultySelectionButton> enumerator = panel.difficultyButtons.GetEnumerator();
				while (enumerator.MoveNext())
				{
					DifficultySelectionButton current = enumerator.Current;
					if (!((Object)(object)current != (Object)null) || ((Object)current).name.StartsWith("RC_Mode_") || current.Difficulty != item)
					{
						continue;
					}
					_clickingForMode = true;
					try
					{
						Action onClicked = ((ButtonInteractionHook)current).OnClicked;
						if (onClicked != null)
						{
							onClicked.Invoke();
						}
					}
					finally
					{
						_clickingForMode = false;
					}
					break;
				}
				RefreshSelection(panel);
				ShowModeDetails(panel, ButtonOf(mode));
				RefreshLobbyIcons();
			}
			catch (Exception value)
			{
				RunCustomizerMod.Log.Error($"[difficulty ui] {value}");
			}
		}

		private static void RefreshSelection(DifficultiesPanel panel)
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			List<DifficultySelectionButton> difficultyButtons = panel.difficultyButtons;
			if (difficultyButtons == null)
			{
				return;
			}
			Mode selected = Modes.Selected;
			Transform val = null;
			Enumerator<DifficultySelectionButton> enumerator = difficultyButtons.GetEnumerator();
			while (enumerator.MoveNext())
			{
				DifficultySelectionButton current = enumerator.Current;
				if (!((Object)(object)current == (Object)null) && !((Object)current).name.StartsWith("RC_Mode_"))
				{
					if (val == null)
					{
						val = ((Component)current).transform.parent;
					}
					if (selected != Mode.Vanilla)
					{
						((SelectableButton)current).Deselect();
					}
					else if (current.Difficulty == panel.selectedDifficulty)
					{
						((SelectableButton)current).Select();
					}
				}
			}
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			Mode[] buttons = Buttons;
			for (int i = 0; i < buttons.Length; i++)
			{
				Mode mode = buttons[i];
				Transform obj = val.Find("RC_Mode_" + mode);
				DifficultySelectionButton val2 = ((obj != null) ? ((Component)obj).GetComponent<DifficultySelectionButton>() : null);
				if (!((Object)(object)val2 == (Object)null))
				{
					if (selected != Mode.Vanilla && ButtonOf(selected) == mode)
					{
						((SelectableButton)val2).Select();
					}
					else
					{
						((SelectableButton)val2).Deselect();
					}
				}
			}
		}

		private static void ShowModeDetails(DifficultiesPanel panel, Mode mode)
		{
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				foreach (LocalizeStringEvent item in Localizers(panel))
				{
					if ((Object)(object)item != (Object)null)
					{
						((Behaviour)item).enabled = false;
					}
				}
				Mode mode2 = ((mode == Mode.Random) ? RandomMode : mode);
				var (key, key2) = mode2 switch
				{
					Mode.Random => ("rn", "rd"), 
					Mode.Custom => ("cn", "cd"), 
					_ => ("bn", "bd"), 
				};
				if ((Object)(object)panel.difficultyNameText != (Object)null)
				{
					panel.difficultyNameText.text = Strings.Get(key);
					((Graphic)panel.difficultyNameText).color = NameColors[mode2];
				}
				SetText(panel.difficultyDescriptionLocalizer, Strings.Get(key2));
				SetText(panel.difficultyExplanationLocalizer, (mode2 == Mode.Random) ? RangesText() : "");
				LocalizeStringEvent difficultyGoldBonusLocalizer = panel.difficultyGoldBonusLocalizer;
				if (difficultyGoldBonusLocalizer != null)
				{
					((Component)difficultyGoldBonusLocalizer).gameObject.SetActive(false);
				}
				ShowRows(panel, mode);
			}
			catch (Exception value)
			{
				RunCustomizerMod.Log.Error($"[difficulty ui] {value}");
			}
		}

		private static string RangesText()
		{
			return R("enemy", Modes.RangeEnemy) + "  ·  " + R("spawn", Modes.RangeSpawnSpeed) + "\n" + R("count", Modes.RangeEnemyCount) + "  ·  " + R("health", Modes.RangeHealth);
			static string R(string key, (float Min, float Max) r)
			{
				return $"{Strings.Get(key)} x{r.Min.ToString("0.##", CultureInfo.InvariantCulture)}–{r.Max.ToString("0.##", CultureInfo.InvariantCulture)}";
			}
		}

		private static void FlipRandomIcon(DifficultiesPanel panel)
		{
			Transform val = null;
			Enumerator<DifficultySelectionButton> enumerator = panel.difficultyButtons.GetEnumerator();
			while (enumerator.MoveNext())
			{
				DifficultySelectionButton current = enumerator.Current;
				if ((Object)(object)current != (Object)null)
				{
					val = ((Component)current).transform.parent;
					break;
				}
			}
			object obj;
			if (val == null)
			{
				obj = null;
			}
			else
			{
				Transform obj2 = val.Find("RC_Mode_" + Mode.Random);
				if (obj2 == null)
				{
					obj = null;
				}
				else
				{
					DifficultySelectionButton component = ((Component)obj2).GetComponent<DifficultySelectionButton>();
					obj = ((component != null) ? component.iconImage : null);
				}
			}
			Image val2 = (Image)obj;
			if ((Object)(object)val2 != (Object)null)
			{
				MelonCoroutines.Start(Flip(val2, Icon(RandomMode)));
			}
		}

		private static IEnumerator Flip(Image image, Sprite to)
		{
			Transform t = ((Component)image).transform;
			for (float time = 0f; time < 0.12f; time += Time.unscaledDeltaTime)
			{
				if (!((Object)(object)image != (Object)null))
				{
					break;
				}
				t.localScale = new Vector3(1f - time / 0.12f, 1f, 1f);
				yield return null;
			}
			if ((Object)(object)image == (Object)null)
			{
				yield break;
			}
			image.sprite = to;
			for (float time = 0f; time < 0.12f; time += Time.unscaledDeltaTime)
			{
				if (!((Object)(object)image != (Object)null))
				{
					break;
				}
				float num = time / 0.12f;
				t.localScale = new Vector3(num, 1f + 0.12f * Mathf.Sin(num * (float)Math.PI), 1f);
				yield return null;
			}
			if ((Object)(object)image != (Object)null)
			{
				t.localScale = Vector3.one;
			}
		}

		private static IEnumerable<LocalizeStringEvent> Localizers(DifficultiesPanel p)
		{
			return (IEnumerable<LocalizeStringEvent>)(object)new LocalizeStringEvent[4] { p.difficultyNameLocalizer, p.difficultyDescriptionLocalizer, p.difficultyExplanationLocalizer, p.difficultyGoldBonusLocalizer };
		}

		private static void SetText(LocalizeStringEvent loc, string text)
		{
			if (!((Object)(object)loc == (Object)null))
			{
				TMP_Text val = ((Component)loc).GetComponent<TMP_Text>() ?? ((Component)loc).GetComponentInChildren<TMP_Text>(true);
				if ((Object)(object)val != (Object)null)
				{
					val.text = text;
				}
			}
		}

		private static void BuildRows(DifficultiesPanel panel)
		{
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Expected O, but got Unknown
			//IL_0097: 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_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			LocalizeStringEvent difficultyExplanationLocalizer = panel.difficultyExplanationLocalizer;
			Transform val = ((difficultyExplanationLocalizer != null) ? ((Component)difficultyExplanationLocalizer).transform : null);
			if ((Object)(object)val == (Object)null)
			{
				RunCustomizerMod.Log.Warning("[difficulty ui] no details area — settings rows unavailable");
				return;
			}
			Transform val2 = FindSelectorRow();
			if ((Object)(object)val2 == (Object)null)
			{
				RunCustomizerMod.Log.Warning("[difficulty ui] no selector row to copy — settings rows unavailable");
				return;
			}
			GameObject val3 = new GameObject("RC_Rows", (Type[])(object)new Type[1] { Il2CppType.Of<RectTransform>() });
			RectTransform component = val3.GetComponent<RectTransform>();
			((Transform)component).SetParent(val, false);
			Vector2 val4 = default(Vector2);
			((Vector2)(ref val4))..ctor(0.5f, 0f);
			component.anchorMax = val4;
			component.anchorMin = val4;
			component.pivot = new Vector2(0.5f, 1f);
			component.anchoredPosition = new Vector2(0f, -20f);
			component.sizeDelta = new Vector2(1000f, 320f);
			VerticalLayoutGroup obj = val3.AddComponent<VerticalLayoutGroup>();
			((LayoutGroup)obj).childAlignment = (TextAnchor)1;
			((HorizontalOrVerticalLayoutGroup)obj).childControlHeight = false;
			((HorizontalOrVerticalLayoutGroup)obj).childControlWidth = false;
			((HorizontalOrVerticalLayoutGroup)obj).childForceExpandHeight = false;
			((HorizontalOrVerticalLayoutGroup)obj).childForceExpandWidth = false;
			Rows.RemoveAll((Row r) => (Object)(object)r.Selector == (Object)null);
			List<Selectable> list = new List<Selectable> { AddRow(val3.transform, val2, Mode.Random, "kind", new Row
			{
				Options = () => new List<string>
				{
					Strings.Get("kind_all"),
					Strings.Get("kind_base")
				},
				Current = () => Strings.Get(RunCustomizerMod.RandomBaseOnly.Value ? "kind_base" : "kind_all"),
				Apply = delegate(string option)
				{
					bool flag = option == Strings.Get("kind_base");
					if (RunCustomizerMod.RandomBaseOnly.Value != flag)
					{
						RunCustomizerMod.RandomBaseOnly.Value = flag;
						RunCustomizerMod.Save();
						Mode selected = Modes.Selected;
						if ((selected == Mode.Random || selected == Mode.RandomBase) ? true : false)
						{
							Modes.Selected = RandomMode;
							Modes.SyncLobby();
							RefreshLobbyIcons();
						}
						ShowModeDetails(panel, Mode.Random);
						FlipRandomIcon(panel);
					}
				}
			}) };
			List<Selectable> list2 = new List<Selectable>();
			list2.Add(AddValueRow(val3.transform, val2, "enemy", RunCustomizerMod.CustomEnemy, new float[16]
			{
				0.5f, 0.75f, 1f, 1.1f, 1.25f, 1.5f, 1.75f, 2f, 2.05f, 2.25f,
				2.5f, 2.75f, 3f, 3.5f, 4f, 4.5f
			}));
			list2.Add(AddValueRow(val3.transform, val2, "spawn", RunCustomizerMod.CustomSpawnSpeed, new float[9] { 0.75f, 1f, 1.25f, 1.43f, 1.75f, 2f, 2.5f, 3f, 4f }));
			list2.Add(AddValueRow(val3.transform, val2, "count", RunCustomizerMod.CustomEnemyCount, new float[6] { 0.75f, 1f, 1.25f, 1.5f, 1.75f, 2f }));
			list2.Add(AddValueRow(val3.transform, val2, "health", RunCustomizerMod.CustomHealth, new float[7] { 0.1f, 0.25f, 0.5f, 0.75f, 1f, 1.25f, 1.5f }));
			List<Selectable> list3 = list2;
			LinkVertically(list);
			LinkVertically(list3);
			RegisterInputReceivers(panel, list, list3);
			val3.SetActive(false);
		}

		private static Selectable AddValueRow(Transform container, Transform template, string labelKey, MelonPreferences_Entry<float> entry, float[] values)
		{
			return AddRow(container, template, Mode.Custom, labelKey, new Row
			{
				Options = delegate
				{
					List<float> list = new List<float>(values);
					if (!list.Contains(entry.Value))
					{
						list.Add(entry.Value);
						list.Sort();
					}
					return list.ConvertAll<string>(Format);
				},
				Current = () => Format(entry.Value),
				Apply = delegate(string option)
				{
					if (float.TryParse(option.TrimStart('x'), NumberStyles.Float, CultureInfo.InvariantCulture, out var result) && entry.Value != result)
					{
						entry.Value = result;
						RunCustomizerMod.Save();
						if (Modes.Selected == Mode.Custom)
						{
							Modes.Reroll();
						}
					}
				}
			});
		}

		private static Transform FindSelectorRow()
		{
			foreach (Selector item in Resources.FindObjectsOfTypeAll<Selector>())
			{
				Transform parent = ((Component)item).transform.parent;
				if ((Object)(object)parent != (Object)null && (Object)(object)((Component)parent).GetComponentInChildren<TMP_Text>(true) != (Object)null && !((Object)parent).name.StartsWith("RC"))
				{
					return parent;
				}
			}
			return null;
		}

		private static Selectable AddRow(Transform container, Transform template, Mode owner, string labelKey, Row row)
		{
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			GameObject obj = Object.Instantiate<GameObject>(((Component)template).gameObject, container);
			((Object)obj).name = "RC_" + labelKey;
			obj.SetActive(true);
			StripGameLogic(obj);
			obj.GetComponent<RectTransform>().sizeDelta = new Vector2(1000f, 80f);
			Selector componentInChildren = obj.GetComponentInChildren<Selector>(true);
			bool flag = false;
			foreach (TMP_Text componentsInChild in obj.GetComponentsInChildren<TMP_Text>(true))
			{
				if (componentsInChild.transform.IsChildOf(((Component)componentInChildren).transform))
				{
					continue;
				}
				if (flag)
				{
					Object.Destroy((Object)(object)((Component)componentsInChild).gameObject);
					continue;
				}
				flag = true;
				foreach (LocalizeStringEvent component in ((Component)componentsInChild).GetComponents<LocalizeStringEvent>())
				{
					Object.DestroyImmediate((Object)(object)component);
				}
				Strings.Bind(componentsInChild, labelKey);
			}
			componentInChildren.localizationMode = (LocalizationMode)2;
			if ((Object)(object)componentInChildren.valueLocalizeString != (Object)null)
			{
				((Behaviour)componentInChildren.valueLocalizeString).enabled = false;
			}
			AddArrow(componentInChildren, componentInChildren.previousButton, "<");
			AddArrow(componentInChildren, componentInChildren.nextButton, ">");
			row.Owner = owner;
			row.Selector = componentInChildren;
			componentInChildren.OnOptionSelected += Action<string>.op_Implicit((Action<string>)delegate(string option)
			{
				try
				{
					row.Apply(option);
				}
				catch (Exception value)
				{
					RunCustomizerMod.Log.Error($"[difficulty ui] {value}");
				}
			});
			Rows.Add(row);
			Show(row);
			return (Selectable)(object)componentInChildren;
		}

		private static void AddArrow(Selector selector, Button button, string glyph)
		{
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)button == (Object)null || (Object)(object)selector.valueText == (Object)null)
			{
				return;
			}
			GameObject val = Object.Instantiate<GameObject>(((Component)selector.valueText).gameObject, ((Component)button).transform);
			((Object)val).name = "RC_Arrow";
			foreach (LocalizeStringEvent component2 in val.GetComponents<LocalizeStringEvent>())
			{
				Object.DestroyImmediate((Object)(object)component2);
			}
			TMP_Text component = val.GetComponent<TMP_Text>();
			component.text = glyph;
			component.alignment = (TextAlignmentOptions)514;
			((Graphic)component).raycastTarget = false;
			RectTransform rectTransform = component.rectTransform;
			rectTransform.anchorMin = Vector2.zero;
			rectTransform.anchorMax = Vector2.one;
			Vector2 offsetMin = (rectTransform.offsetMax = Vector2.zero);
			rectTransform.offsetMin = offsetMin;
			Transform transform = ((Component)button).transform;
			if ((transform.lossyScale.x < 0f) ^ (transform.right.x < 0f))
			{
				((Transform)rectTransform).localScale = new Vector3(-1f, 1f, 1f);
			}
		}

		private static void Show(Row row)
		{
			List<string> val = new List<string>();
			foreach (string item in row.Options())
			{
				val.Add(item);
			}
			string text = row.Current();
			row.Selector.SetOptions(val);
			row.Selector.SetSelectedOption(text);
			row.Selector.SetNonLocalizedValueText(text);
		}

		private static string Format(float v)
		{
			return "x" + v.ToString("0.##", CultureInfo.InvariantCulture);
		}

		private static void ShowRows(DifficultiesPanel panel, Mode? owner)
		{
			LocalizeStringEvent difficultyExplanationLocalizer = panel.difficultyExplanationLocalizer;
			Transform val = ((difficultyExplanationLocalizer != null) ? ((Component)difficultyExplanationLocalizer).transform.Find("RC_Rows") : null);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			bool flag = false;
			foreach (Row row in Rows)
			{
				if (!((Object)(object)row.Selector == (Object)null))
				{
					bool flag2 = owner == row.Owner;
					((Component)((Component)row.Selector).transform.parent).gameObject.SetActive(flag2);
					if (flag2)
					{
						flag = true;
						Show(row);
					}
				}
			}
			((Component)val).gameObject.SetActive(flag);
			if (flag)
			{
				RandomWizard.AllowClicks(val, ((Component)panel).transform);
			}
		}

		public static void LobbyIconPostfix(DifficultySelectionDisplayer __instance)
		{
			try
			{
				if (Modes.Selected != Mode.Vanilla && Modes.IsHost && (Object)(object)__instance.iconImage != (Object)null)
				{
					__instance.iconImage.sprite = Icon(Modes.Selected);
				}
			}
			catch (Exception value)
			{
				RunCustomizerMod.Log.Error($"[lobby icon] {value}");
			}
		}

		private static void RefreshLobbyIcons()
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			MainMenuManager instance = NetworkSingleton<MainMenuManager>.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			foreach (DifficultySelectionDisplayer item in Object.FindObjectsOfType<DifficultySelectionDisplayer>())
			{
				item.DisplayDifficulty(instance.CurrentDifficulty.Value);
			}
		}

		public static void RunIconPostfix(DifficultyLevelVisualizer __instance)
		{
			try
			{
				if (Modes.InRun && (Object)(object)__instance.iconImage != (Object)null)
				{
					__instance.iconImage.sprite = Icon(Modes.Selected);
				}
			}
			catch (Exception value)
			{
				RunCustomizerMod.Log.Error($"[run icon] {value}");
			}
		}

		private static void LinkVertically(List<Selectable> rows)
		{
			for (int i = 0; i < rows.Count; i++)
			{
				Navigation navigation = rows[i].navigation;
				navigation.mode = (Mode)4;
				navigation.selectOnUp = ((i > 0) ? rows[i - 1] : null);
				navigation.selectOnDown = ((i < rows.Count - 1) ? rows[i + 1] : null);
				navigation.selectOnLeft = null;
				navigation.selectOnRight = null;
				rows[i].navigation = navigation;
			}
		}

		private static void RegisterInputReceivers(DifficultiesPanel panel, params List<Selectable>[] groups)
		{
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			UIPanel val = ((Component)panel).GetComponent<UIPanel>() ?? ((Component)panel).GetComponentInChildren<UIPanel>(true);
			if ((Object)(object)val == (Object)null)
			{
				RunCustomizerMod.Log.Warning("[difficulty ui] no UIPanel — arrow keys won't change values");
				return;
			}
			UIPanel val2 = val;
			if (val2.inputReceivers == null)
			{
				List<IUIInputReceiver> val3 = (val2.inputReceivers = new List<IUIInputReceiver>());
			}
			for (int i = 0; i < groups.Length; i++)
			{
				foreach (Selectable item in groups[i])
				{
					val.inputReceivers.Add((IUIInputReceiver)(((object)((Il2CppObjectBase)item).TryCast<IUIInputReceiver>()) ?? ((object)new IUIInputReceiver(((Il2CppObjectBase)item).Pointer))));
				}
			}
		}

		private static void StripGameLogic(GameObject row)
		{
			foreach (MonoBehaviour component in row.GetComponents<MonoBehaviour>())
			{
				if (((MemberInfo)((Object)component).GetIl2CppType()).Name != "Image")
				{
					Object.DestroyImmediate((Object)(object)component);
				}
			}
			foreach (TextReplacer componentsInChild in row.GetComponentsInChildren<TextReplacer>(true))
			{
				Object.DestroyImmediate((Object)(object)componentsInChild);
			}
		}

		private static void MuteSerializedClicks(Button button)
		{
			if (!((Object)(object)button == (Object)null))
			{
				for (int i = 0; i < ((UnityEventBase)button.onClick).GetPersistentEventCount(); i++)
				{
					((UnityEventBase)button.onClick).SetPersistentListenerState(i, (UnityEventCallState)0);
				}
			}
		}
	}
	public class RunCustomizerMod : MelonMod
	{
		public static Instance Log;

		private static MelonPreferences_Category _category;

		internal static MelonPreferences_Entry<int> ModeEntry;

		internal static MelonPreferences_Entry<float> CustomEnemy;

		internal static MelonPreferences_Entry<float> CustomSpawnSpeed;

		internal static MelonPreferences_Entry<float> CustomEnemyCount;

		internal static MelonPreferences_Entry<float> CustomHealth;

		internal static MelonPreferences_Entry<bool> RandomBaseOnly;

		private int _hooksInstalled;

		private int _hooksTotal;

		private float _nextLobbySync;

		public override void OnInitializeMelon()
		{
			Log = ((MelonBase)this).LoggerInstance;
			_category = MelonPreferences.CreateCategory("RunCustomizer", "Run Customizer");
			ModeEntry = _category.CreateEntry<int>("DifficultyMode", 0, "Difficulty Mode", "0 = обычная (выбор игры), 1 = случайная, 2 = своя, 3 = случайная из базовых", false, false, (ValueValidator)(object)new ValueRange<int>(0, 3), (string)null);
			CustomEnemy = _category.CreateEntry<float>("CustomEnemyStrength", 2.05f, "Custom: Enemy Strength", "Своя сложность: здоровье и урон врагов (Normal 1.1, Hard 2.05, Nightmare 3)", false, false, (ValueValidator)(object)new ValueRange<float>(0.1f, 10f), (string)null);
			CustomSpawnSpeed = _category.CreateEntry<float>("CustomSpawnSpeed", 1.43f, "Custom: Spawn Speed", "Своя сложность: скорость появления врагов (Normal 1, Hard 1.43, Nightmare 2.5)", false, false, (ValueValidator)(object)new ValueRange<float>(0.1f, 10f), (string)null);
			CustomEnemyCount = _category.CreateEntry<float>("CustomEnemyCount", 1.25f, "Custom: Enemy Count", "Своя сложность: минимум врагов на карте (Normal 1, Hard 1.25, Nightmare 1.5)", false, false, (ValueValidator)(object)new ValueRange<float>(0.1f, 10f), (string)null);
			CustomHealth = _category.CreateEntry<float>("CustomHealthDrops", 0.5f, "Custom: Health Drops", "Своя сложность: шанс аптечек (Normal 1, Hard 0.5, Nightmare 0.25)", false, false, (ValueValidator)(object)new ValueRange<float>(0f, 10f), (string)null);
			RandomBaseOnly = _category.CreateEntry<bool>("RandomBaseOnly", false, "Random: Base Difficulty Only", "Случайная сложность: false — случайно каждый параметр, true — случайно одна из Normal/Hard/Nightmare", false, false, (ValueValidator)null, (string)null);
			_category.SaveToFile(false);
			ApplyPatches();
			Log.Msg($"{_hooksInstalled}/{_hooksTotal} hooks — difficulty mode: {Modes.Selected}");
		}

		internal static void Save()
		{
			_category.SaveToFile(false);
		}

		public override void OnSceneWasInitialized(int buildIndex, string sceneName)
		{
			if ((sceneName == "MainMenu" || sceneName == "StartMenu") ? true : false)
			{
				Modes.OnBackToMenu();
			}
		}

		public override void OnUpdate()
		{
			if (Time.unscaledTime < _nextLobbySync)
			{
				return;
			}
			_nextLobbySync = Time.unscaledTime + 0.5f;
			try
			{
				Modes.SyncLobby();
			}
			catch (Exception ex)
			{
				Log.Error("lobby sync: " + ex.Message);
			}
		}

		private void ApplyPatches()
		{
			Patch(Method(typeof(EnemyDifficultyParameters), "GetMultiplierForDifficulty"), null, "EnemyMultiplierPostfix");
			Patch(Method(typeof(EnemyDifficultyParameters), "GetExtraDifficultyMultiplierForCycle"), null, "CycleMultiplierPostfix");
			Patch(AccessTools.PropertyGetter(typeof(WaveConfiguration), "SpawnIntervalInSecondsForDifficultyAndCycle"), null, "SpawnIntervalPostfix");
			Patch(AccessTools.PropertyGetter(typeof(WaveConfiguration), "MinNumberOfConcurrentEnemiesForDifficulty"), null, "MinEnemiesPostfix");
			Patch(Method(typeof(PickupSpawner), "DeterminePickUp"), "DeterminePickUpPrefix");
			Patch(Method(typeof(RunGoldCalculatorData), "GetGoldMultiplierForDifficulty"), null, "GoldMultiplierPostfix");
			Patch(Method(typeof(MainMenuManager), "PrepareForLevelStart"), null, "OnLevelStartPostfix", typeof(RunCustomizerMod));
			Patch(Method(typeof(DifficultiesPanel), "InitializeButtons"), null, "InitializeButtonsPostfix", typeof(DifficultyUi));
			Patch(Method(typeof(DifficultiesPanel), "Show"), null, "ShowPostfix", typeof(DifficultyUi));
			Patch(Method(typeof(DifficultiesPanel), "ShowDifficultyDetails"), null, "ShowDifficultyDetailsPostfix", typeof(DifficultyUi));
			Patch(Method(typeof(DifficultySelectionDisplayer), "DisplayDifficulty"), null, "LobbyIconPostfix", typeof(DifficultyUi));
			Patch(Method(typeof(DifficultyLevelVisualizer), "Start"), null, "RunIconPostfix", typeof(DifficultyUi));
			Patch(Method(typeof(MissionStatsPanel), "Show"), null, "MissionStatsPostfix", typeof(StatsUi));
			Patch(Method(typeof(GameOverMissionStatsPanel), "SetValues"), null, "GameOverStatsPostfix", typeof(StatsUi));
			Patch(Method(typeof(CharacterSelector), "InitializeCharacterButtons"), null, "InitializePostfix", typeof(RandomWizard));
			Patch(Method(typeof(CharacterPanel), "OnSelected"), "PanelSelectedPrefix", null, typeof(RandomWizard));
			Patch(Method(typeof(CharacterPanel), "OnHighlightStarted"), "PanelHighlightPrefix", null, typeof(RandomWizard));
			Patch(Method(typeof(CharacterSelector), "MakeInitialSelection"), null, "InitialSelectionPostfix", typeof(RandomWizard));
			Patch(Method(typeof(CharacterPanel), "Display"), null, "DisplayPostfix", typeof(RandomWizard));
			Patch(Method(typeof(MainMenuManager), "StartLevelOnServerRpc"), "StartLevelPrefix", null, typeof(RandomWizard));
			Patch(Method(typeof(MainMenuManager), "PrepareForLevelStart"), "PrepareForLevelStartPrefix", null, typeof(RandomWizard));
		}

		private static void OnLevelStartPostfix()
		{
			try
			{
				Modes.OnLevelStart();
			}
			catch (Exception value)
			{
				Log.Error($"level start: {value}");
			}
		}

		private static MethodInfo Method(Type type, string name)
		{
			return AccessTools.Method(type, name, (Type[])null, (Type[])null);
		}

		private void Patch(MethodInfo original, string prefix = null, string postfix = null, Type patchClass = null)
		{
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			if ((object)patchClass == null)
			{
				patchClass = typeof(DifficultyHooks);
			}
			_hooksTotal++;
			string text = ((original != null) ? (original.DeclaringType?.Name + "." + original.Name) : ((prefix ?? postfix) + " target"));
			try
			{
				if (original == null)
				{
					throw new MissingMethodException(text);
				}
				List<string> list = SharedCodeGuard.FindMethodsSharingCode(original);
				if (list.Count > 0)
				{
					Log.Warning($"skipped {text}: its native code is shared with {list.Count} other method(s)");
				}
				else
				{
					((MelonBase)this).HarmonyInstance.Patch((MethodBase)original, (prefix == null) ? ((HarmonyMethod)null) : new HarmonyMethod(AccessTools.Method(patchClass, prefix, (Type[])null, (Type[])null)), (postfix == null) ? ((HarmonyMethod)null) : new HarmonyMethod(AccessTools.Method(patchClass, postfix, (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					_hooksInstalled++;
				}
			}
			catch (Exception ex)
			{
				Log.Error($"failed to hook {text} — this part is disabled. {ex.GetType().Name}: {ex.Message}");
			}
		}
	}
	internal enum Mode
	{
		Vanilla,
		Random,
		Custom,
		RandomBase
	}
	internal struct Params
	{
		public float Enemy;

		public float SpawnSpeed;

		public float EnemyCount;

		public float HealthDrops;

		public override string ToString()
		{
			return $"enemies x{Enemy:0.##}, spawn speed x{SpawnSpeed:0.##}, enemy count x{EnemyCount:0.##}, health drops x{HealthDrops:0.##}";
		}
	}
	internal static class Modes
	{
		public static readonly float[] BaseEnemy = new float[3] { 1.1f, 2.05f, 3f };

		public static readonly float[] BaseSpawnSpeed = new float[3] { 1f, 1.4285715f, 2.5f };

		public static readonly float[] BaseEnemyCount = new float[3] { 1f, 1.25f, 1.5f };

		public static readonly float[] BaseHealth = new float[3] { 1f, 0.5f, 0.25f };

		public static readonly (float Min, float Max) RangeEnemy = (Min: 0.5f, Max: 4.5f);

		public static readonly (float Min, float Max) RangeSpawnSpeed = (Min: 0.75f, Max: 4f);

		public static readonly (float Min, float Max) RangeEnemyCount = (Min: 0.75f, Max: 2f);

		public static readonly (float Min, float Max) RangeHealth = (Min: 0.1f, Max: 1.5f);

		private static (Params Values, Difficulty Base)? _pending;

		private static bool _inLevel;

		public static Mode Selected
		{
			get
			{
				return (Mode)Math.Clamp(RunCustomizerMod.ModeEntry.Value, 0, 3);
			}
			set
			{
				if (Selected != value)
				{
					RunCustomizerMod.ModeEntry.Value = (int)value;
					RunCustomizerMod.Save();
					_pending = null;
				}
			}
		}

		public static Params Custom => new Params
		{
			Enemy = RunCustomizerMod.CustomEnemy.Value,
			SpawnSpeed = RunCustomizerMod.CustomSpawnSpeed.Value,
			EnemyCount = RunCustomizerMod.CustomEnemyCount.Value,
			HealthDrops = RunCustomizerMod.CustomHealth.Value
		};

		public static bool InRun { get; private set; }

		public static Params Active { get; private set; }

		public static (Params Values, Difficulty Base) Pending
		{
			get
			{
				(Params, Difficulty) valueOrDefault = _pending.GetValueOrDefault();
				if (!_pending.HasValue)
				{
					valueOrDefault = Roll();
					_pending = valueOrDefault;
					return valueOrDefault;
				}
				return valueOrDefault;
			}
		}

		public static bool IsHost
		{
			get
			{
				NetworkManager singleton = NetworkManager.Singleton;
				if ((Object)(object)singleton != (Object)null)
				{
					return singleton.IsServer;
				}
				return false;
			}
		}

		public static Mode LastRunMode { get; private set; }

		public static Difficulty RunDifficulty
		{
			get
			{
				//IL_0016: 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_0019: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Unknown result type (might be due to invalid IL or missing references)
				try
				{
					RunData instance = SingletonPersistent<RunData>.Instance;
					return (Difficulty)((instance != null) ? ((int)instance.SelectedDifficulty) : 0);
				}
				catch
				{
					return (Difficulty)0;
				}
			}
		}

		public static void Reroll()
		{
			_pending = null;
		}

		private static (Params, Difficulty) Roll()
		{
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			Random random = new Random();
			switch (Selected)
			{
			case Mode.Random:
			{
				Params obj = new Params
				{
					Enemy = Pick(random, RangeEnemy),
					SpawnSpeed = Pick(random, RangeSpawnSpeed),
					EnemyCount = Pick(random, RangeEnemyCount),
					HealthDrops = Pick(random, RangeHealth)
				};
				return (obj, NearestBase(obj));
			}
			case Mode.Custom:
				return (Custom, NearestBase(Custom));
			case Mode.RandomBase:
			{
				int num = HighestUnlocked();
				Difficulty val = (Difficulty)random.Next(0, num + 1);
				return (Of(val), val);
			}
			default:
				return (Of((Difficulty)0), (Difficulty)0);
			}
		}

		private static float Pick(Random rnd, (float Min, float Max) r)
		{
			return (float)Math.Round((double)r.Min + rnd.NextDouble() * (double)(r.Max - r.Min), 2);
		}

		public static Params Of(Difficulty d)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Expected I4, but got Unknown
			int num = (int)d;
			return new Params
			{
				Enemy = BaseEnemy[num],
				SpawnSpeed = BaseSpawnSpeed[num],
				EnemyCount = BaseEnemyCount[num],
				HealthDrops = BaseHealth[num]
			};
		}

		public static float Score(Params p)
		{
			return (Position(p.Enemy, BaseEnemy) + Position(p.SpawnSpeed, BaseSpawnSpeed) + Position(p.EnemyCount, BaseEnemyCount) + Position(p.HealthDrops, BaseHealth)) / 4f;
		}

		private static float Position(float v, float[] bases)
		{
			int num = (((bases[2] > bases[0]) ? (!(v < bases[1])) : (!(v > bases[1]))) ? 1 : 0);
			float num2 = bases[num];
			float num3 = bases[num + 1];
			return (float)num + (v - num2) / (num3 - num2);
		}

		private static Difficulty NearestBase(Params p)
		{
			return (Difficulty)Math.Clamp((int)Math.Round(Score(p)), 0, HighestUnlocked());
		}

		public static int HighestUnlocked()
		{
			int result = 0;
			for (int i = 1; i <= 2; i++)
			{
				try
				{
					DifficultyConfiguration val = DifficultyConfigurationRepository.Get((Difficulty)i);
					if ((Object)(object)val != (Object)null && !val.IsLockedByProgression())
					{
						result = i;
					}
				}
				catch
				{
				}
			}
			return result;
		}

		public static void OnLevelStart()
		{
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			InRun = false;
			_inLevel = true;
			LastRunMode = Mode.Vanilla;
			if (Selected != Mode.Vanilla && IsHost)
			{
				Params item = Pending.Values;
				_pending = null;
				LastRunMode = Selected;
				if (Selected == Mode.RandomBase)
				{
					RunCustomizerMod.Log.Msg($"run difficulty: {RunDifficulty} (random from base)");
					return;
				}
				Active = item;
				InRun = true;
				RunCustomizerMod.Log.Msg($"run difficulty ({Selected}, base {RunDifficulty}): {item}");
			}
		}

		public static void OnBackToMenu()
		{
			InRun = (_inLevel = false);
		}

		public static void SyncLobby()
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			if (_inLevel || Selected == Mode.Vanilla || !IsHost)
			{
				return;
			}
			MainMenuManager instance = NetworkSingleton<MainMenuManager>.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			Difficulty item = Pending.Base;
			if (instance.CurrentDifficulty.Value != item)
			{
				instance.SetDifficulty(item);
				RunSetupPreferences instance2 = SingletonPersistent<RunSetupPreferences>.Instance;
				if (instance2 != null)
				{
					instance2.SetPreferredDifficulty(item);
				}
			}
		}
	}
	internal static class RandomWizard
	{
		private enum Kind
		{
			Instant,
			Surprise
		}

		private static readonly Dictionary<Kind, string> Names = new Dictionary<Kind, string>
		{
			[Kind.Instant] = "RC_RandomWizard",
			[Kind.Surprise] = "RC_SurpriseWizard"
		};

		private static bool _pendingSurprise;

		private static Kind? _shown;

		private static bool _cycling;

		private static bool _rolling;

		private static bool _seatCycling;

		private static int _appliedFrame = -1;

		private static Color? _aliasColor;

		private static readonly List<SkinId> Skins = new List<SkinId>();

		private static readonly Random Rnd = new Random();

		private static int _lastSkin = -1;

		private static bool IsOurs(CharacterButton b)
		{
			if ((Object)(object)b != (Object)null)
			{
				return ((Object)b).name.StartsWith("RC_");
			}
			return false;
		}

		public static void InitializePostfix(CharacterSelector __instance)
		{
			try
			{
				AddButtons(__instance);
			}
			catch (Exception value)
			{
				RunCustomizerMod.Log.Error($"[random wizard] {value}");
			}
		}

		private static void AddButtons(CharacterSelector selector)
		{
			//IL_0072: 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)
			//IL_02a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d7: Unknown result type (might be due to invalid IL or missing references)
			List<CharacterButton> initializedCharacterButtons = selector.initializedCharacterButtons;
			if (initializedCharacterButtons == null || initializedCharacterButtons.Count == 0)
			{
				return;
			}
			Skins.Clear();
			Enumerator<CharacterButton> enumerator = initializedCharacterButtons.GetEnumerator();
			while (enumerator.MoveNext())
			{
				CharacterButton current = enumerator.Current;
				if ((Object)(object)((current != null) ? current.resource : null) != (Object)null && !current.resource.Disabled)
				{
					Skins.Add(current.resource.DefaultSkin);
				}
			}
			CharacterButton obj = RandomCandidate(selector, null);
			CharacterResource val = ((obj != null) ? obj.resource : null);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			CharacterButton val2 = initializedCharacterButtons[initializedCharacterButtons.Count - 1];
			Transform parent = ((Component)val2).transform.parent;
			int num = 0;
			enumerator = initializedCharacterButtons.GetEnumerator();
			while (enumerator.MoveNext())
			{
				CharacterButton current2 = enumerator.Current;
				if ((Object)(object)current2 != (Object)null && (Object)(object)((Component)current2).transform.parent == (Object)(object)parent)
				{
					num = Math.Max(num, ((Component)current2).transform.GetSiblingIndex());
				}
			}
			Kind[] array = new Kind[2]
			{
				Kind.Instant,
				Kind.Surprise
			};
			foreach (Kind kind in array)
			{
				CharacterButton val3 = Find(selector, kind);
				if ((Object)(object)val3 == (Object)null)
				{
					GameObject obj2 = Object.Instantiate<GameObject>(((Component)val2).gameObject, parent);
					((Object)obj2).name = Names[kind];
					obj2.transform.SetSiblingIndex(++num);
					val3 = obj2.GetComponent<CharacterButton>();
					Selectable component = obj2.GetComponent<Selectable>();
					if ((Object)(object)component != (Object)null)
					{
						Navigation navigation = component.navigation;
						navigation.mode = (Mode)3;
						component.navigation = navigation;
					}
					Kind k = kind;
					val3.OnSelected += Action.op_Implicit((Action)delegate
					{
						OnClicked(selector, k);
					});
					ButtonEventHook eventHook = val3.eventHook;
					if (eventHook != null)
					{
						eventHook.OnHighlightStarted += Action.op_Implicit((Action)delegate
						{
							try
							{
								Show(PanelOf(selector), k);
							}
							catch (Exception value)
							{
								RunCustomizerMod.Log.Error($"[random wizard] {value}");
							}
						});
					}
					for (int num2 = parent.childCount - 1; num2 >= 0; num2--)
					{
						Transform child = parent.GetChild(num2);
						if (((Component)child).gameObject.activeSelf && (Object)(object)((Component)child).GetComponent<RectTransform>() != (Object)null && (Object)(object)((Component)child).GetComponent<CharacterButton>() == (Object)null)
						{
							((Component)child).gameObject.SetActive(false);
							break;
						}
					}
				}
				((Component)val3).gameObject.SetActive(true);
				val3.Initialize(val);
				Image iconImage = val3.iconImage;
				if ((Object)(object)iconImage != (Object)null)
				{
					Rect rect = ((Graphic)iconImage).rectTransform.rect;
					Vector2 size = ((Rect)(ref rect)).size;
					float aspect = ((size.x > 1f && size.y > 1f) ? (size.x / size.y) : 0f);
					iconImage.sprite = Sprites.Get((kind == Kind.Instant) ? "random_wizard.png" : "surprise_wizard.png", aspect);
					iconImage.preserveAspect = false;
				}
				if ((Object)(object)val3.extraGoldBonusPanel != (Object)null)
				{
					val3.extraGoldBonusPanel.SetActive(false);
				}
			}
			ShowSelected(selector, _pendingSurprise);
		}

		private static void OnClicked(CharacterSelector selector, Kind kind)
		{
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (_rolling)
				{
					return;
				}
				CharacterPanel panel = PanelOf(selector);
				if (kind == Kind.Surprise)
				{
					_pendingSurprise = true;
					CharacterSelectorSoundPlayer soundPlayer = selector.soundPlayer;
					if (soundPlayer != null)
					{
						soundPlayer.PlayCharacterSelectSound();
					}
					ShowSelected(selector, surpriseSelected: true);
					Show(panel, Kind.Surprise);
					if (!_seatCycling)
					{
						MelonCoroutines.Start(CycleSeatModel());
					}
				}
				else
				{
					LocalPlayer instance = SingletonPersistent<LocalPlayer>.Instance;
					CharacterId? exclude = ((instance != null) ? new CharacterId?(instance.GetSelectedCharacter()) : ((CharacterId?)null));
					CharacterButton val = RandomCandidate(selector, exclude);
					if ((Object)(object)val != (Object)null)
					{
						MelonCoroutines.Start(Roulette(selector, panel, val));
					}
				}
			}
			catch (Exception value)
			{
				RunCustomizerMod.Log.Error($"[random wizard] {value}");
			}
		}

		public static void PanelHighlightPrefix()
		{
			if (!_rolling)
			{
				_shown = null;
			}
		}

		public static void PanelSelectedPrefix(CharacterPanel __instance)
		{
			if (_rolling)
			{
				return;
			}
			_shown = null;
			if (_pendingSurprise)
			{
				_pendingSurprise = false;
				CharacterSelector selector = __instance.selector;
				if ((Object)(object)selector != (Object)null)
				{
					ShowSelected(selector, surpriseSelected: false);
				}
			}
		}

		public static void InitialSelectionPostfix(CharacterSelector __instance)
		{
			if (_pendingSurprise)
			{
				ShowSelected(__instance, surpriseSelected: true);
				Show(PanelOf(__instance), Kind.Surprise);
			}
		}

		private static void ShowSelected(CharacterSelector selector, bool surpriseSelected)
		{
			CharacterButton val = Find(selector, Kind.Instant);
			if ((Object)(object)((val != null) ? val.animator : null) != (Object)null)
			{
				val.animator.SetBool(CharacterButton.IsSelected, false);
			}
			CharacterButton val2 = Find(selector, Kind.Surprise);
			if ((Object)(object)((val2 != null) ? val2.animator : null) != (Object)null)
			{
				val2.animator.SetBool(CharacterButton.IsSelected, surpriseSelected);
			}
			CharacterButton selectedCharacterButton = selector.selectedCharacterButton;
			if ((Object)(object)((selectedCharacterButton != null) ? selectedCharacterButton.animator : null) != (Object)null && !IsOurs(selectedCharacterButton))
			{
				selectedCharacterButton.animator.SetBool(CharacterButton.IsSelected, !surpriseSelected);
			}
		}

		public static void DisplayPostfix(CharacterPanel __instance, bool isLocked)
		{
			try
			{
				if (_shown.HasValue)
				{
					Show(__instance, _shown.Value);
				}
				else
				{
					Restore(__instance, isLocked);
				}
			}
			catch (Exception value)
			{
				RunCustomizerMod.Log.Error($"[random wizard] {value}");
			}
		}

		private static void Show(CharacterPanel panel, Kind kind)
		{
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_019b: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)panel == (Object)null)
			{
				return;
			}
			_shown = kind;
			LoreAndDetailsDisplayToggler loreAndDetailsDisplayToggler = panel.loreAndDetailsDisplayToggler;
			if ((Object)(object)loreAndDetailsDisplayToggler != (Object)null)
			{
				loreAndDetailsDisplayToggler.ToggleLoreDisplay(true);
				ButtonPrompt toggleLoreDisplayPrompt = loreAndDetailsDisplayToggler.toggleLoreDisplayPrompt;
				if (toggleLoreDisplayPrompt != null)
				{
					((Component)toggleLoreDisplayPrompt).gameObject.SetActive(false);
				}
			}
			CharacterLoreDisplay characterLoreDisplay = panel.characterLoreDisplay;
			bool flag = kind == Kind.Surprise && _pendingSurprise;
			CharacterInfoDisplay characterInfoDisplay = panel.characterInfoDisplay;
			object loc;
			if (characterInfoDisplay == null)
			{
				loc = null;
			}
			else
			{
				CharacterUIVisualizer characterVisualizer = characterInfoDisplay.characterVisualizer;
				loc = ((characterVisualizer != null) ? characterVisualizer.nameLocalizer : null);
			}
			SetText((LocalizeStringEvent)loc, Strings.Get((kind == Kind.Instant) ? "rw" : "sw"));
			SetText((characterLoreDisplay != null) ? characterLoreDisplay.aliasLocalizer : null, Strings.Get(flag ? "w_chosen" : "w_alias"));
			object obj;
			if (characterLoreDisplay == null)
			{
				obj = null;
			}
			else
			{
				LocalizeStringEvent aliasLocalizer = characterLoreDisplay.aliasLocalizer;
				obj = ((aliasLocalizer != null) ? ((Component)aliasLocalizer).GetComponent<TMP_Text>() : null);
			}
			TMP_Text val = (TMP_Text)obj;
			if ((Object)(object)val != (Object)null)
			{
				Color valueOrDefault = _aliasColor.GetValueOrDefault();
				if (!_aliasColor.HasValue)
				{
					valueOrDefault = ((Graphic)val).color;
					_aliasColor = valueOrDefault;
				}
				((Graphic)val).color = (Color)(flag ? new Color(0.25f, 0.95f, 0.3f) : _aliasColor.Value);
			}
			SetText((characterLoreDisplay != null) ? characterLoreDisplay.descriptionLocalizer : null, Strings.Get((kind == Kind.Instant) ? "w_instant" : "w_surprise"));
			CharacterRankDisplay characterRankDisplay = panel.characterRankDisplay;
			if (characterRankDisplay != null)
			{
				((Component)characterRankDisplay).gameObject.SetActive(false);
			}
			LockedCharacterInfoDisplay lockedCharacterInfoDisplay = panel.lockedCharacterInfoDisplay;
			if (lockedCharacterInfoDisplay != null)
			{
				((Component)lockedCharacterInfoDisplay).gameObject.SetActive(false);
			}
			LocalCharacterDisplay localCharacterDisplay = panel.localCharacterDisplay;
			RawImage val2 = ((localCharacterDisplay != null) ? localCharacterDisplay.image : null);
			if ((Object)(object)val2 != (Object)null)
			{
				((Graphic)val2).color = Color.black;
			}
			if (!_cycling && !_rolling)
			{
				MelonCoroutines.Start(CycleSilhouettes(panel));
			}
		}

		private static void Restore(CharacterPanel panel, bool isLocked)
		{
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			CharacterLoreDisplay characterLoreDisplay = panel.characterLoreDisplay;
			LocalizeStringEvent[] array = new LocalizeStringEvent[3];
			CharacterInfoDisplay characterInfoDisplay = panel.characterInfoDisplay;
			object obj;
			if (characterInfoDisplay == null)
			{
				obj = null;
			}
			else
			{
				CharacterUIVisualizer characterVisualizer = characterInfoDisplay.characterVisualizer;
				obj = ((characterVisualizer != null) ? characterVisualizer.nameLocalizer : null);
			}
			array[0] = (LocalizeStringEvent)obj;
			array[1] = ((characterLoreDisplay != null) ? characterLoreDisplay.aliasLocalizer : null);
			array[2] = ((characterLoreDisplay != null) ? characterLoreDisplay.descriptionLocalizer : null);
			LocalizeStringEvent[] array2 = (LocalizeStringEvent[])(object)array;
			foreach (LocalizeStringEvent val in array2)
			{
				if ((Object)(object)val != (Object)null && !((Behaviour)val).enabled)
				{
					((Behaviour)val).enabled = true;
					val.RefreshString();
				}
			}
			object obj2;
			if (characterLoreDisplay == null)
			{
				obj2 = null;
			}
			else
			{
				LocalizeStringEvent aliasLocalizer = characterLoreDisplay.aliasLocalizer;
				obj2 = ((aliasLocalizer != null) ? ((Component)aliasLocalizer).GetComponent<TMP_Text>() : null);
			}
			TMP_Text val2 = (TMP_Text)obj2;
			if ((Object)(object)val2 != (Object)null && _aliasColor.HasValue)
			{
				((Graphic)val2).color = _aliasColor.Value;
			}
			LoreAndDetailsDisplayToggler loreAndDetailsDisplayToggler = panel.loreAndDetailsDisplayToggler;
			if (loreAndDetailsDisplayToggler != null)
			{
				ButtonPrompt toggleLoreDisplayPrompt = loreAndDetailsDisplayToggler.toggleLoreDisplayPrompt;
				if (toggleLoreDisplayPrompt != null)
				{
					((Component)toggleLoreDisplayPrompt).gameObject.SetActive(true);
				}
			}
			LocalCharacterDisplay localCharacterDisplay = panel.localCharacterDisplay;
			if (localCharacterDisplay != null)
			{
				localCharacterDisplay.SetDarkened(isLocked);
			}
		}

		private static void SetText(LocalizeStringEvent loc, string text)
		{
			if (!((Object)(object)loc == (Object)null))
			{
				((Behaviour)loc).enabled = false;
				TMP_Text val = ((Component)loc).GetComponent<TMP_Text>() ?? ((Component)loc).GetComponentInChildren<TMP_Text>(true);
				if ((Object)(object)val != (Object)null)
				{
					val.text = text;
				}
			}
		}

		private static SkinId NextSkin()
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			int num = Rnd.Next(Skins.Count);
			if (num == _lastSkin && Skins.Count > 1)
			{
				num = (num + 1) % Skins.Count;
			}
			_lastSkin = num;
			return Skins[num];
		}

		private static void ShowSilhouette(CharacterPanel panel, SkinId skin)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			LocalCharacterDisplay localCharacterDisplay = panel.localCharacterDisplay;
			localCharacterDisplay.Display(skin);
			if ((Object)(object)localCharacterDisplay.image != (Object)null)
			{
				((Graphic)localCharacterDisplay.image).color = Color.black;
			}
		}

		private static IEnumerator CycleSilhouettes(CharacterPanel panel)
		{
			_cycling = true;
			while (_shown.HasValue && !_rolling && (Object)(object)panel != (Object)null && ((Behaviour)panel).isActiveAndEnabled && Skins.Count > 0)
			{
				try
				{
					ShowSilhouette(panel, NextSkin());
				}
				catch (Exception ex)
				{
					RunCustomizerMod.Log.Error("[random wizard] silhouettes: " + ex.Message);
					break;
				}
				yield return (object)new WaitForSeconds(0.35f);
			}
			_cycling = false;
		}

		private static IEnumerator Roulette(CharacterSelector selector, CharacterPanel panel, CharacterButton target)
		{
			_rolling = true;
			Show(panel, Kind.Instant);
			float delay = 0.05f;
			while (delay < 0.3f && (Object)(object)panel != (Object)null && Skins.Count > 0)
			{
				try
				{
					ShowSilhouette(panel, NextSkin());
				}
				catch (Exception ex)
				{
					RunCustomizerMod.Log.Error("[random wizard] roulette: " + ex.Message);
					break;
				}
				yield return (object)new WaitForSeconds(delay);
				delay *= 1.18f;
			}
			_rolling = false;
			_shown = null;
			try
			{
				selector.OnCharacterButtonSelected(target);
			}
			catch (Exception value)
			{
				RunCustomizerMod.Log.Error($"[random wizard] {value}");
			}
		}

		private static IEnumerator CycleSeatModel()
		{
			_seatCycling = true;
			while (_pendingSurprise && Skins.Count > 0)
			{
				try
				{
					PlayerSeatUpdater instance = Singleton<PlayerSeatUpdater>.Instance;
					PlayerSeat obj = ((instance != null) ? instance.GetLocalPlayerSeat() : null);
					if (obj != null)
					{
						CharacterModelVisualizer characterModelVisualizer = obj.characterModelVisualizer;
						if (characterModelVisualizer != null)
						{
							characterModelVisualizer.Show(NextSkin(), false, false);
						}
					}
				}
				catch
				{
				}
				yield return (object)new WaitForSeconds(0.6f);
			}
			_seatCycling = false;
			try
			{
				PlayerSeatUpdater instance2 = Singleton<PlayerSeatUpdater>.Instance;
				if (instance2 != null)
				{
					instance2.RefreshSeats();
				}
			}
			catch
			{
			}
		}

		internal static void AllowClicks(Transform from, Transform upTo)
		{
			Transform parent = from.parent;
			while ((Object)(object)parent != (Object)null && (Object)(object)parent != (Object)(object)upTo)
			{
				CanvasGroup component = ((Component)parent).GetComponent<CanvasGroup>();
				if (!((Object)(object)component == (Object)null))
				{
					component.blocksRaycasts = true;
					component.interactable = true;
				}
				parent = parent.parent;
			}
		}

		public static void StartLevelPrefix()
		{
			if (Modes.IsHost)
			{
				ApplySurprise();
			}
		}

		public static void PrepareForLevelStartPrefix()
		{
			if (!Modes.IsHost)
			{
				ApplySurprise();
			}
		}

		private static void ApplySurprise()
		{
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			if (!_pendingSurprise || _appliedFrame == Time.frameCount)
			{
				return;
			}
			_appliedFrame = Time.frameCount;
			try
			{
				CharacterSelector val = null;
				foreach (CharacterSelector item in Resources.FindObjectsOfTypeAll<CharacterSelector>())
				{
					if ((Object)(object)item != (Object)null)
					{
						List<CharacterButton> initializedCharacterButtons = item.initializedCharacterButtons;
						if (initializedCharacterButtons != null && initializedCharacterButtons.Count > 0)
						{
							val = item;
							break;
						}
					}
				}
				CharacterButton val2 = (((Object)(object)val != (Object)null) ? RandomCandidate(val, null) : null);
				if ((Object)(object)val2 == (Object)null)
				{
					RunCustomizerMod.Log.Warning("[random wizard] no wizard to pick");
					return;
				}
				val2.ConfirmSelectedCharacter();
				RunCustomizerMod.Log.Msg($"[random wizard] surprise: {val2.resource.Id}");
			}
			catch (Exception value)
			{
				RunCustomizerMod.Log.Error($"[random wizard] surprise failed, keeping the current wizard: {value}");
			}
		}

		private static CharacterButton RandomCandidate(CharacterSelector selector, CharacterId? exclude)
		{
			List<CharacterButton> list = new List<CharacterButton>();
			Enumerator<CharacterButton> enumerator = selector.initializedCharacterButtons.GetEnumerator();
			while (enumerator.MoveNext())
			{
				CharacterButton current = enumerator.Current;
				CharacterResource val = ((current != null) ? current.resource : null);
				if (!((Object)(object)val == (Object)null) && !IsOurs(current) && !val.Disabled && !val.IsLockedByProgression())
				{
					list.Add(current);
				}
			}
			if (exclude.HasValue && list.Count > 1)
			{
				list.RemoveAll((CharacterButton b) => b.resource.Id == exclude.Value);
			}
			if (list.Count <= 0)
			{
				return null;
			}
			return list[Rnd.Next(list.Count)];
		}

		private static CharacterPanel PanelOf(CharacterSelector selector)
		{
			foreach (CharacterPanel item in Resources.FindObjectsOfTypeAll<CharacterPanel>())
			{
				if ((Object)(object)item != (Object)null && (Object)(object)item.selector != (Object)null && ((Il2CppObjectBase)item.selector).Pointer == ((Il2CppObjectBase)selector).Pointer)
				{
					return item;
				}
			}
			return null;
		}

		private static CharacterButton Find(CharacterSelector selector, Kind kind)
		{
			List<CharacterButton> initializedCharacterButtons = selector.initializedCharacterButtons;
			if (initializedCharacterButtons == null || initializedCharacterButtons.Count == 0 || (Object)(object)initializedCharacterButtons[0] == (Object)null)
			{
				return null;
			}
			Transform val = ((Component)initializedCharacterButtons[0]).transform.parent.Find(Names[kind]);
			if (!((Object)(object)val != (Object)null))
			{
				return null;
			}
			return ((Component)val).GetComponent<CharacterButton>();
		}
	}
	internal static class SharedCodeGuard
	{
		private static Dictionary<IntPtr, List<IntPtr>> _methodsByCode;

		public static List<string> FindMethodsSharingCode(MethodBase generatedMethod)
		{
			List<string> list = new List<string>();
			if (!(Il2CppInteropUtils.GetIl2CppMethodInfoPointerFieldForGeneratedMethod(generatedMethod)?.GetValue(null) is IntPtr intPtr) || intPtr == IntPtr.Zero)
			{
				return list;
			}
			IntPtr intPtr2 = Marshal.ReadIntPtr(intPtr);
			if (intPtr2 == IntPtr.Zero)
			{
				return list;
			}
			if (_methodsByCode == null)
			{
				_methodsByCode = BuildIndex();
			}
			if (!_methodsByCode.TryGetValue(intPtr2, out var value))
			{
				return list;
			}
			foreach (IntPtr item in value)
			{
				if (item != intPtr)
				{
					list.Add(Describe(item));
				}
			}
			return list;
		}

		private static string Describe(IntPtr method)
		{
			IntPtr intPtr = IL2CPP.il2cpp_method_get_class(method);
			string text = IL2CPP.il2cpp_class_get_namespace_(intPtr);
			return (string.IsNullOrEmpty(text) ? IL2CPP.il2cpp_class_get_name_(intPtr) : (text + "." + IL2CPP.il2cpp_class_get_name_(intPtr))) + "::" + IL2CPP.il2cpp_method_get_name_(method);
		}

		private unsafe static Dictionary<IntPtr, List<IntPtr>> BuildIndex()
		{
			Dictionary<IntPtr, List<IntPtr>> dictionary = new Dictionary<IntPtr, List<IntPtr>>();
			uint num = 0u;
			IntPtr* ptr = IL2CPP.il2cpp_domain_get_assemblies(IL2CPP.il2cpp_domain_get(), ref num);
			for (uint num2 = 0u; num2 < num; num2++)
			{
				IntPtr intPtr = IL2CPP.il2cpp_assembly_get_image(ptr[num2]);
				uint num3 = IL2CPP.il2cpp_image_get_class_count(intPtr);
				for (uint num4 = 0u; num4 < num3; num4++)
				{
					IntPtr intPtr2 = IL2CPP.il2cpp_image_get_class(intPtr, num4);
					if (intPtr2 == IntPtr.Zero)
					{
						continue;
					}
					IntPtr zero = IntPtr.Zero;
					IntPtr intPtr3;
					while ((intPtr3 = IL2CPP.il2cpp_class_get_methods(intPtr2, ref zero)) != IntPtr.Zero)
					{
						IntPtr intPtr4 = Marshal.ReadIntPtr(intPtr3);
						if (!(intPtr4 == IntPtr.Zero))
						{
							if (!dictionary.TryGetValue(intPtr4, out var value))
							{
								value = (dictionary[intPtr4] = new List<IntPtr>(1));
							}
							value.Add(intPtr3);
						}
					}
				}
			}
			return dictionary;
		}
	}
	internal static class Sprites
	{
		private static readonly Dictionary<string, Sprite> Cache = new Dictionary<string, Sprite>();

		private static readonly Dictionary<string, Texture2D> Textures = new Dictionary<string, Texture2D>();

		public static Sprite Get(string file)
		{
			return Get(file, 0f);
		}

		public static Sprite Get(string file, float aspect)
		{
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			string key = ((aspect > 0f) ? $"{file}@{aspect:0.###}" : file);
			if (Cache.TryGetValue(key, out var value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			Texture2D val = Texture(file);
			float num = ((Texture)val).width;
			float num2 = ((Texture)val).height;
			if (aspect > 0f)
			{
				if (num / num2 > aspect)
				{
					num = num2 * aspect;
				}
				else
				{
					num2 = num / aspect;
				}
			}
			Rect val2 = default(Rect);
			((Rect)(ref val2))..ctor(((float)((Texture)val).width - num) / 2f, ((float)((Texture)val).height - num2) / 2f, num, num2);
			Sprite val3 = Sprite.Create(val, val2, new Vector2(0.5f, 0.5f), 100f);
			((Object)val3).hideFlags = (HideFlags)61;
			return Cache[key] = val3;
		}

		private static Texture2D Texture(string file)
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Expected O, but got Unknown
			if (Textures.TryGetValue(file, out var value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			using Stream stream = typeof(Sprites).Assembly.GetManifestResourceStream(file);
			byte[] array = new byte[stream.Length];
			stream.Read(array, 0, array.Length);
			Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false)
			{
				hideFlags = (HideFlags)61
			};
			ImageConversion.LoadImage(val, Il2CppStructArray<byte>.op_Implicit(array));
			return Textures[file] = val;
		}
	}
	internal static class StatsUi
	{
		private const string RowPrefix = "RC_Stat_";

		private static readonly Dictionary<IntPtr, Color> OriginalColors = new Dictionary<IntPtr, Color>();

		public static void MissionStatsPostfix(MissionStatsPanel __instance)
		{
			Apply(__instance.difficultyEntry);
		}

		public static void GameOverStatsPostfix(GameOverMissionStatsPanel __instance)
		{
			Apply(__instance.difficultyEntry);
		}

		private static void Apply(DifficultyEntry entry)
		{
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if ((Object)(object)entry == (Object)null)
				{
					return;
				}
				Transform transform = ((Component)entry).transform;
				Transform parent = transform.parent;
				if ((Object)(object)parent != (Object)null)
				{
					for (int num = parent.childCount - 1; num >= 0; num--)
					{
						if (((Object)parent.GetChild(num)).name.StartsWith("RC_Stat_"))
						{
							Object.Destroy((Object)(object)((Component)parent.GetChild(num)).gameObject);
						}
					}
				}
				Mode lastRunMode = Modes.LastRunMode;
				TMP_Text difficultyText = entry.difficultyText;
				if ((uint)(lastRunMode - 1) > 1u)
				{
					if ((Object)(object)entry.difficultyLocalizer != (Object)null && !((Behaviour)entry.difficultyLocalizer).enabled)
					{
						((Behaviour)entry.difficultyLocalizer).enabled = true;
						entry.difficultyLocalizer.RefreshString();
					}
					if ((Object)(object)difficultyText != (Object)null && OriginalColors.TryGetValue(((Il2CppObjectBase)difficultyText).Pointer, out var value))
					{
						((Graphic)difficultyText).color = value;
					}
					return;
				}
				if ((Object)(object)difficultyText != (Object)null)
				{
					if (!OriginalColors.ContainsKey(((Il2CppObjectBase)difficultyText).Pointer))
					{
						OriginalColors[((Il2CppObjectBase)difficultyText).Pointer] = ((Graphic)difficultyText).color;
					}
					if ((Object)(object)entry.difficultyLocalizer != (Object)null)
					{
						((Behaviour)entry.difficultyLocalizer).enabled = false;
					}
					difficultyText.text = Strings.Get((lastRunMode == Mode.Custom) ? "cn" : "rn");
					((Graphic)difficultyText).color = DifficultyUi.NameColors[lastRunMode];
				}
				if ((Object)(object)entry.icon != (Object)null)
				{
					entry.icon.sprite = DifficultyUi.Icon(lastRunMode);
				}
				if (!((Object)(object)parent == (Object)null))
				{
					Params active = Modes.Active;
					int siblingIndex = transform.GetSiblingIndex();
					AddRow(entry, "enemy", active.Enemy, ++siblingIndex);
					AddRow(entry, "spawn", active.SpawnSpeed, ++siblingIndex);
					AddRow(entry, "count", active.EnemyCount, ++siblingIndex);
					AddRow(entry, "health", active.HealthDrops, ++siblingIndex);
				}
			}
			catch (Exception value2)
			{
				RunCustomizerMod.Log.Error($"[stats] {value2}");
			}
		}

		private static void AddRow(DifficultyEntry entry, string labelKey, float multiplier, int siblingIndex)
		{
			//IL_0178: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = Object.Instantiate<GameObject>(((Component)entry).gameObject, ((Component)entry).transform.parent);
			((Object)val).name = "RC_Stat_" + labelKey;
			val.transform.SetSiblingIndex(siblingIndex);
			Object.DestroyImmediate((Object)(object)val.GetComponent<DifficultyEntry>());
			foreach (LocalizeStringEvent componentsInChild in val.GetComponentsInChildren<LocalizeStringEvent>(true))
			{
				Object.DestroyImmediate((Object)(object)componentsInChild);
			}
			Transform transform = ((Component)entry).transform;
			TMP_Text difficultyText = entry.difficultyText;
			string text = PathFrom(transform, (difficultyText != null) ? difficultyText.transform : null);
			Transform transform2 = ((Component)entry).transform;
			Image icon = entry.icon;
			string text2 = PathFrom(transform2, (icon != null) ? ((Component)icon).transform : null);
			object obj;
			if (text == null)
			{
				obj = null;
			}
			else
			{
				Transform obj2 = Find(val.transform, text);
				obj = ((obj2 != null) ? ((Component)obj2).GetComponent<TMP_Text>() : null);
			}
			TMP_Text val2 = (TMP_Text)obj;
			string text3 = "x" + multiplier.ToString("0.##", CultureInfo.InvariantCulture);
			bool flag = false;
			foreach (TMP_Text componentsInChild2 in val.GetComponentsInChildren<TMP_Text>(true))
			{
				if ((!((Object)(object)val2 != (Object)null) || !(((Il2CppObjectBase)componentsInChild2).Pointer == ((Il2CppObjectBase)val2).Pointer)) && !flag)
				{
					flag = true;
					componentsInChild2.text = Strings.Get(labelKey);
				}
			}
			if ((Object)(object)val2 != (Object)null)
			{
				val2.text = (flag ? text3 : (Strings.Get(labelKey) + " " + text3));
				((Graphic)val2).color = Color.white;
			}
			object obj3;
			if (text2 == null)
			{
				obj3 = null;
			}
			else
			{
				Transform obj4 = Find(val.transform, text2);
				obj3 = ((obj4 != null) ? ((Component)obj4).GetComponent<Image>() : null);
			}
			Image val3 = (Image)obj3;
			if ((Object)(object)val3 != (Object)null)
			{
				((Graphic)val3).color = new Color(1f, 1f, 1f, 0f);
			}
		}

		private static string PathFrom(Transform root, Transform child)
		{
			if ((Object)(object)child == (Object)null)
			{
				return null;
			}
			if ((Object)(object)child == (Object)(object)root)
			{
				return "";
			}
			List<string> list = new List<string>();
			Transform val = child;
			while ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)root)
			{
				list.Insert(0, ((Object)val).name);
				val = val.parent;
			}
			return string.Join("/", list);
		}

		private static Transform Find(Transform root, string path)
		{
			if (!(path == ""))
			{
				return root.Find(path);
			}
			return root;
		}
	}
	internal static class Strings
	{
		public const string RandomName = "rn";

		public const string RandomDesc = "rd";

		public const string CustomName = "cn";

		public const string CustomDesc = "cd";

		public const string BaseName = "bn";

		public const string BaseDesc = "bd";

		public const string Enemy = "enemy";

		public const string Spawn = "spawn";

		public const string Count = "count";

		public const string Health = "health";

		public const string RandomWizard = "rw";

		public const string SurpriseWizard = "sw";

		public const string WizardAlias = "w_alias";

		public const string WizardDescInstant = "w_instant";

		public const string WizardDescSurprise = "w_surprise";

		public const string RandomKind = "kind";

		public const string KindAll = "kind_all";

		public const string KindBase = "kind_base";

		public const string WizardChosen = "w_chosen";

		private static readonly string[] Keys = new string[19]
		{
			"rn", "rd", "cn", "cd", "bn", "bd", "enemy", "spawn", "count", "health",
			"rw", "sw", "w_alias", "w_instant", "w_surprise", "kind", "kind_all", "kind_base", "w_chosen"
		};

		private static readonly Dictionary<string, string[]> Table = new Dictionary<string, string[]>
		{
			["en"] = new string[19]
			{
				"Random", "Every setting is rolled on its own, from easier than the easiest difficulty to harsher than the hardest. You'll see what you got when the run starts.", "Custom", "Set the difficulty yourself. The gold bonus follows the resulting difficulty.", "Random", "One of the difficulties you've unlocked is picked at random when the run starts.", "Enemy Strength", "Spawn Speed", "Enemy Count", "Health Drops",
				"Random Wizard", "Surprise Wizard", "Any of your unlocked wizards", "Click it and the roulette picks a random unlocked wizard for you.", "A random wizard is picked when the run starts. Until then the lobby shows your previous wizard — only you see random ones flicker on your seat.", "What's random", "Every setting", "Base difficulty", "Picked — you'll see who it is when the run starts"
			},
			["ru"] = new string[19]
			{
				"Случайная", "Каждый параметр выпадает отдельно — от легче самой лёгкой угрозы до жёстче самой сильной. Что выпало, узнаете в начале забега.", "Своя", "Настройте сложность сами. Бонус золота — по итоговой сложности.", "Случайная", "В начале забега случайно выпадет одна из открытых сложностей.", "Сила врагов", "Скорость появления", "Число врагов", "Аптечки",
				"Случайный волшебник", "Волшебник-сюрприз", "Любой из открытых волшебников", "Нажмите — рулетка выберет случайного открытого волшебника.", "Случайный волшебник выпадет в начале забега. До тех пор в лобби стоит прежний — только у вас на месте мелькают случайные.", "Что выпадает", "Каждый параметр", "Базовая сложность", "Выбран — узнаете, кто это, в начале забега"
			},
			["uk"] = new string[19]
			{
				"Випадкова", "Кожен параметр випадає окремо — від легшого за найлегшу загрозу до жорсткішого за найсильнішу. Що випало, дізнаєтеся на початку забігу.", "Своя", "Налаштуйте складність самі. Бонус золота — за підсумковою складністю.", "Випадкова", "На початку забігу випадково випаде одна з відкритих складностей.", "Сила ворогів", "Швидкість появи", "Кількість ворогів", "Аптечки",
				"Випадковий чарівник", "Чарівник-сюрприз", "Будь-який із відкритих чарівників", "Натисніть — рулетка обере випадкового відкритого чарівника.", "Випадковий чарівник випаде на початку забігу. До того в лобі стоїть попередній — лише у вас на місці миготять випадкові.", "Що випадає", "Кожен параметр", "Базова складність", "Обрано — дізнаєтеся, хто це, на початку забігу"
			},
			["de"] = new string[19]
			{
				"Zufällig", "Jeder Wert wird einzeln ausgewürfelt – von leichter als die leichteste bis härter als die schwerste Schwierigkeit. Was du bekommst, siehst du zu Beginn des Durchlaufs.", "Eigene", "Stelle die Schwierigkeit selbst ein. Der Goldbonus richtet sich nach der Gesamtschwierigkeit.", "Zufällig", "Zu Beginn des Durchlaufs wird eine deiner freigeschalteten Schwierigkeiten zufällig gewählt.", "Gegnerstärke", "Spawn-Tempo", "Gegneranzahl", "Heiltränke",
				"Zufälliger Magier", "Überraschungsmagier", "Einer deiner freigeschalteten Magier", "Klicke – das Roulette wählt einen zufälligen freigeschalteten Magier für dich.", "Ein zufälliger Magier wird zu Beginn des Durchlaufs gewählt. Bis dahin zeigt die Lobby deinen bisherigen Magier – nur du siehst auf deinem Platz zufällige aufblitzen.", "Was ist zufällig", "Jeder Wert", "Grundschwierigkeit", "Gewählt – wer es ist, siehst du zu Beginn des Durchlaufs"
			},
			["fr"] = new string[19]
			{
				"Aléatoire", "Chaque paramètre est tiré séparément, de plus facile que la difficulté la plus basse à plus dur que la plus haute. Vous découvrirez le résultat au début de la partie.", "Personnalisée", "Réglez la difficulté vous-même. Le bonus d'or suit la difficulté obtenue.", "Aléatoire", "Une des difficultés débloquées est tirée au sort au début de la partie.", "Force des ennemis", "Vitesse d'apparition", "Nombre d'ennemis", "Soins",
				"Mage aléatoire", "Mage surprise", "N'importe lequel de vos mages débloqués", "Cliquez et la roulette choisit pour vous un mage débloqué au hasard.", "Un mage est tiré au sort au début de la partie. D'ici là, le salon affiche votre mage précédent — vous seul voyez des mages au hasard défiler à votre place.", "Ce qui est aléatoire", "Chaque paramètre", "Difficulté de base", "Choisi — vous saurez qui c'est au début de la partie"
			},
			["it"] = new string[19]
			{
				"Casuale", "Ogni parametro viene estratto a parte, da più facile della difficoltà più bassa a più duro della più alta. Scoprirai cosa è uscito all'inizio della partita.", "Personalizzata", "Imposta tu la difficoltà. Il bonus d'oro segue la difficoltà risultante.", "Casuale", "All'inizio della partita viene estratta una delle difficoltà sbloccate.", "Forza nemici", "Velocità di comparsa", "Numero di nemici", "Cure",
				"Mago casuale", "Mago sorpresa", "Uno qualsiasi dei tuoi maghi sbloccati", "Clicca e la roulette sceglie per te un mago sbloccato a caso.", "Il mago viene estratto all'inizio della partita. Fino ad allora la lobby mostra il mago precedente: solo tu vedi maghi casuali alternarsi al tuo posto.", "Cosa è casuale", "Ogni parametro", "Difficoltà base", "Scelto: scoprirai chi è all'inizio della partita"
			},
			["nl"] = new string[19]
			{
				"Willekeurig", "Elke waarde wordt apart geloot, van makkelijker dan de makkelijkste tot zwaarder dan de zwaarste moeilijkheid. Wat je krijgt, zie je aan het begin van de run.", "Aangepast", "Stel de moeilijkheid zelf in. De goudbonus volgt de uiteindelijke moeilijkheid.", "Willekeurig", "Aan het begin van de run wordt een van je ontgrendelde moeilijkheden geloot.", "Vijandkracht", "Spawnsnelheid", "Aantal vijanden", "Genezing",
				"Willekeurige tovenaar", "Verrassingstovenaar", "Een van je ontgrendelde tovenaars", "Klik en de roulette kiest een willekeurige ontgrendelde tovenaar voor je.", "De tovenaar wordt aan het begin van de run geloot. Tot dan toont de lobby je vorige tovenaar — alleen jij ziet willekeurige tovenaars op je plek flitsen.", "Wat is willekeurig", "Elke waarde", "Basismoeilijkheid", "Gekozen — wie het is, zie je aan het begin van de run"
			},
			["pl"] = new string[19]
			{
				"Losowy", "Każdy parametr losowany jest osobno — od łatwiejszego niż najniższy poziom do trudniejszego niż najwyższy. Co wypadło, zobaczysz na początku wyprawy.", "Własny", "Ustaw poziom trudności sam. Premia złota zależy od końcowej trudności.", "Losowy", "Na początku wyprawy losowany jest jeden z odblokowanych poziomów trudności.", "Siła wrogów", "Tempo pojawiania", "Liczba wrogów", "Apteczki",
				"Losowy mag", "Mag-niespodzianka", "Dowolny z odblokowanych magów", "Kliknij, a ruletka wybierze losowego odblokowanego maga.", "Mag zostanie wylosowany na początku wyprawy. Do tego czasu w poczekalni stoi poprzedni — tylko ty widzisz, jak na twoim miejscu migają losowi magowie.", "Co jest losowe", "Każdy parametr", "Poziom bazowy", "Wybrano — kto to, zobaczysz na początku wyprawy"
			},
			["pt-br"] = new string[19]
			{
				"Aleatória", "Cada parâmetro é sorteado separadamente, de mais fácil que a menor dificuldade a mais difícil que a maior. Você verá o resultado no início da partida.", "Personalizada", "Ajuste a dificuldade você mesmo. O bônus de ouro segue a dificuldade resultante.", "Aleatória", "Uma das dificuldades desbloqueadas é sorteada no início da partida.", "Força dos inimigos", "Velocidade de surgimento", "Quantidade de inimigos", "Curas",
				"Mago aleatório", "Mago surpresa", "Qualquer um dos seus magos desbloqueados", "Clique e a roleta escolhe um mago desbloqueado aleatório para você.", "Um mago é sorteado no início da partida. Até lá, o lobby mostra seu mago anterior — só você vê magos aleatórios piscando no seu lugar.", "O que é aleatório", "Cada parâmetro", "Dificuldade base", "Escolhido — você saberá quem é no início da partida"
			},
			["pt"] = new string[19]
			{
				"Aleatória", "Cada parâmetro é sorteado em separado, de mais fácil do que a dificuldade mais baixa a mais difícil do que a mais alta. Verá o resultado no início da partida.", "Personalizada", "Defina a dificuldade a seu gosto. O bónus de ouro acompanha a dificuldade resultante.", "Aleatória", "No início da partida é sorteada uma das dificuldades desbloqueadas.", "Força dos inimigos", "Velocidade de aparecimento", "Número de inimigos", "Curas",
				"Mago aleatório", "Mago surpresa", "Qualquer um dos seus magos desbloqueados", "Clique e a roleta escolhe um mago desbloqueado ao acaso.", "O mago é sorteado no início da partida. Até lá, o lobby mostra o seu mago anterior — só você vê magos aleatórios a piscar no seu lugar.", "O que é aleatório", "Cada parâmetro", "Dificuldade base", "Escolhido — saberá quem é no início da partida"
			},
			["es"] = new string[19]
			{
				"Aleatoria", "Cada parámetro se sortea por separado, desde más fácil que la dificultad más baja hasta más duro que la más alta. Verás el resultado al empezar la partida.", "Personalizada", "Ajusta la dificultad tú mismo. La bonificación de oro sigue a la dificultad resultante.", "Aleatoria", "Al empezar la partida se sortea una de las dificultades desbloqueadas.", "Fuerza enemiga", "Velocidad de aparición", "Cantidad de enemigos", "Curación",
				"Mago aleatorio", "Mago sorpresa", "Cualquiera de tus magos desbloqueados", "Haz clic y la ruleta elegirá un mago desbloqueado al azar.", "El mago se sortea al empezar la partida. Hasta entonces, la sala muestra tu mago anterior; solo tú ves magos al azar parpadear en tu sitio.", "Qué es aleatorio", "Cada parámetro", "Dificultad base", "Elegido: sabrás quién es al empezar la partida"
			},
			["ja"] = new string[19]
			{
				"ランダム", "各項目が個別に抽選されます。最も易しい難易度より易しいものから、最も難しい難易度より厳しいものまで。結果はラン開始時に分かります。", "カスタム", "難易度を自分で設定します。ゴールドボーナスは最終的な難易度に応じて決まります。", "ランダム", "ラン開始時に、解放済みの難易度からランダムに1つ選ばれます。", "敵の強さ", "出現速度", "敵の数", "回復アイテム",
				"ランダム魔法使い", "サプライズ魔法使い", "解放済みの魔法使いからどれか", "クリックするとルーレットが解放済みの魔法使いをランダムに選びます。", "魔法使いはラン開始時にランダムで決まります。それまでロビーには前の魔法使いが表示され、あなたの席でだけランダムな魔法使いが切り替わります。", "ランダムの対象", "各項目", "基本難易度", "選択済み — 誰になるかはラン開始時に分かります"
			},
			["ko"] = new string[19]
			{
				"무작위", "각 항목이 따로 뽑힙니다. 가장 쉬운 난이도보다 쉬운 값부터 가장 어려운 난이도보다 가혹한 값까지. 결과는 런이 시작될 때 알 수 있습니다.", "사용자 지정", "난이도를 직접 설정하세요. 골드 보너스는 최종 난이도를 따릅니다.", "무작위", "런이 시작될 때 해금한 난이도 중 하나가 무작위로 선택됩니다.", "적 강도", "출현 속도", "적 수", "회복 아이템",
				"무작위 마법사", "깜짝 마법사", "해금한 마법사 중 아무나", "클릭하면 룰렛이 해금한 마법사 중 하나를 무작위로 골라 줍니다.", "마법사는 런이 시작될 때 무작위로 정해집니다. 그때까지 로비에는 이전 마법사가 보이며, 내 자리에서만 무작위 마법사가 번갈아 나타납니다.", "무작위 대상", "각 항목", "기본 난이도", "선택됨 — 누구인지는 런이 시작될 때 알 수 있습니다"
			},
			["zh-hans"] = new string[19]
			{
				"随机", "每项数值分别随机,从比最低难度更简单到比最高难度更严酷。结果在对局开始时揭晓。", "自定义", "自行设置难度。金币加成随最终难度而定。", "随机", "对局开始时,从已解锁的难度中随机选择一个。", "敌人强度", "刷新速度", "敌人数量", "治疗掉落",
				"随机法师", "惊喜法师", "任一已解锁的法师", "点击后,轮盘会为你随机选出一位已解锁的法师。", "法师将在对局开始时随机决定。在此之前大厅显示你之前的法师——只有你会看到自己座位上随机法师轮流闪现。", "随机内容", "每项数值", "基础难度", "已选择——对局开始时揭晓是谁"
			},
			["zh-hant"] = new string[19]
			{
				"隨機", "每項數值分別隨機,從比最低難度更簡單到比最高難度更嚴酷。結果在對局開始時揭曉。", "自訂", "自行設定難度。金幣加成隨最終難度而定。", "隨機", "對局開始時,從已解鎖的難度中隨機選擇一個。", "敵人強度", "出現速度", "敵人數量", "治療掉落",
				"隨機法師", "驚喜法師", "任一已解鎖的法師", "點擊後,輪盤會為你隨機選出一位已解鎖的法師。", "法師將在對局開始時隨機決定。在此之前大廳顯示你之前的法師——只有你會看到自己座位上隨機法師輪流閃現。", "隨機內容", "每項數值", "基礎難度", "已選擇——對局開始時揭曉是誰"
			},
			["th"] = new string[19]
			{
				"ส\u0e38\u0e48ม", "แต\u0e48ละค\u0e48าจะถ\u0e39กส\u0e38\u0e48มแยกก\u0e31น ต\u0e31\u0e49งแต\u0e48ง\u0e48ายกว\u0e48าระด\u0e31บท\u0e35\u0e48ง\u0e48ายท\u0e35\u0e48ส\u0e38ดไปจนถ\u0e36งโหดกว\u0e48าระด\u0e31บท\u0e35\u0e48ยากท\u0e35\u0e48ส\u0e38ด ค\u0e38ณจะร\u0e39\u0e49ผลเม\u0e37\u0e48อเร\u0e34\u0e48มรอบ", "กำหนดเอง", "ต\u0e31\u0e49งค\u0e48าความยากด\u0e49วยต\u0e31วเอง โบน\u0e31สทองจะเป\u0e47นไปตามความยากท\u0e35\u0e48ได\u0e49", "ส\u0e38\u0e48ม", "เม\u0e37\u0e48อเร\u0e34\u0e48มรอบ จะส\u0e38\u0e48มหน\u0e36\u0e48งในระด\u0e31บความยากท\u0e35\u0e48ปลดล\u0e47อกแล\u0e49ว", "ความแข\u0e47งแกร\u0e48งของศ\u0e31ตร\u0e39", "ความเร\u0e47วการเก\u0e34ด", "จำนวนศ\u0e31ตร\u0e39", "ยาฟ\u0e37\u0e49นพล\u0e31ง",
				"น\u0e31กเวทย\u0e4cส\u0e38\u0e48ม", "น\u0e31กเวทย\u0e4cเซอร\u0e4cไพรส\u0e4c", "น\u0e31กเวทย\u0e4cคนไหนก\u0e47ได\u0e49ท\u0e35\u0e48ปลดล\u0e47อกแล\u0e49ว", "คล\u0e34กแล\u0e49ววงล\u0e49อจะส\u0e38\u0e48มเล\u0e37อกน\u0e31กเวทย\u0e4cท\u0e35\u0e48ปลดล\u0e47อกแล\u0e49วให\u0e49ค\u0e38ณ", "น\u0e31กเวทย\u0e4cจะถ\u0e39กส\u0e38\u0e48มเม\u0e37\u0e48อเร\u0e34\u0e48มรอบ ระหว\u0e48างน\u0e31\u0e49นล\u0e47อบบ\u0e35\u0e49จะแสดงน\u0e31กเวทย\u0e4cคนเด\u0e34ม — ม\u0e35เพ\u0e35ยงค\u0e38ณท\u0e35\u0e48เห\u0e47นน\u0e31กเวทย\u0e4cส\u0e38\u0e48มสล\u0e31บไปมาท\u0e35\u0e48ท\u0e35\u0e48น\u0e31\u0e48งของค\u0e38ณ", "อะไรท\u0e35\u0e48ส\u0e38\u0e48ม", "ท\u0e38กค\u0e48า", "ความยากพ\u0e37\u0e49นฐาน", "เล\u0e37อกแล\u0e49ว — จะร\u0e39\u0e49ว\u0e48าเป\u0e47นใครเม\u0e37\u0e48อเร\u0e34\u0e48มรอบ"
			}
		};

		private static string[] _current;

		private static readonly Dictionary<string, bool> FontSupport = new Dictionary<string, bool>();

		private static readonly List<(TMP_Text text, string key)> Bound = new List<(TMP_Text, string)>();

		private static readonly List<Action> Listeners = new List<Action>();

		private static bool _subscribed;

		public static string Get(string key)
		{
			if (_current == null)
			{
				_current = Resolve(null);
			}
			int num = Array.IndexOf(Keys, key);
			if (num < 0 || num >= _current.Length)
			{
				return key;
			}
			return _current[num];
		}

		public static void Bind(TMP_Text text, string key, TMP_Text fontSample = null)
		{
			if (!((Object)(object)text == (Object)null))
			{
				EnsureSubscribed();
				if (_current == null)
				{
					_current = Resolve(fontSample ?? text);
				}
				text.text = Get(key);
				Bound.Add((text, key));
			}
		}

		public static void OnLanguageChanged(Action listener)
		{
			EnsureSubscribed();
			Listeners.Add(listener);
		}

		private static void EnsureSubscribed()
		{
			if (_subscribed)
			{
				return;
			}
			_subscribed = true;
			try
			{
				LocalizationSettings.SelectedLocaleChanged += Action<Locale>.op_Implicit((Action<Locale>)delegate
				{
					Refresh();
				});
			}
			catch (Exception ex)
			{
				RunCustomizerMod.Log.Warning("[lang] can't follow language changes: " + ex.Message);
			}
		}

		private static void Refresh()
		{
			TMP_Text fontSample = null;
			Bound.RemoveAll(((TMP_Text text, string key) b) => (Object)(object)b.text == (Object)null);
			using (List<(TMP_Text, string)>.Enumerator enumerator = Bound.GetEnumerator())
			{
				if (enumerator.MoveNext())
				{
					fontSample = enumerator.Current.Item1;
				}
			}
			_current = Resolve(fontSample);
			foreach (var (val, key) in Bound)
			{
				val.text = Get(key);
			}
			foreach (Action listener in Listeners)
			{
				try
				{
					listener();
				}
				catch (Exception ex)
				{
					RunCustomizerMod.Log.Warning("[lang] " + ex.Message);
				}
			}
		}

		private static string[] Resolve(TMP_Text fontSample)
		{
			string text = "en";
			try
			{
				Locale selectedLocale = LocalizationSettings.SelectedLocale;
				text = ((selectedLocale != null) ? selectedLocale.Identifier.Code : null) ?? "en";
			}
			catch
			{
			}
			string text2 = TableKey(text);
			if (!Table.ContainsKey(text2))
			{
				text2 = "en";
			}
			string[] array = Table[text2];
			if (text2 != "en" && (Object)(object)fontSample != (Object)null && !FontHasAll(fontSample, text2, array))
			{
				RunCustomizerMod.Log.Warning("[lang] font lacks glyphs for '" + text + "', using English");
				text2 = "en";
				array = Table[text2];
			}
			return array;
		}

		private static string TableKey(string code)
		{
			code = code.ToLowerInvariant().Replace('_', '-');
			if (code.StartsWith("zh"))
			{
				if (!code.Contains("hant") && !code.Contains("tw") && !code.Contains("hk"))
				{
					return "zh-hans";
				}
				return "zh-hant";
			}
			if (code.StartsWith("pt"))
			{
				if (!code.Contains("br"))
				{
					return "pt";
				}
				return "pt-br";
			}
			string text = code.Split('-')[0];
			if (!Table.ContainsKey(text))
			{
				return "en";
			}
			return text;
		}

		private static bool FontHasAll(TMP_Text sample, string key, string[] table)
		{
			if (FontSupport.TryGetValue(key, out var value))
			{
				return value;
			}
			value = true;
			try
			{
				TMP_FontAsset font = sample.font;
				if ((Object)(object)font != (Object)null)
				{
					foreach (string text in table)
					{
						foreach (char c in text)
						{
							if (c > '\u007f' && !char.IsWhiteSpace(c) && !font.HasCharacter(c, true, true))
							{
								value = false;
								break;
							}
						}
					}
				}
			}
			catch
			{
				value = true;
			}
			FontSupport[key] = value;
			return value;
		}
	}
}