Decompiled source of RunCustomizer v1.1.2

Mods/RunCustomizer.dll

Decompiled 3 weeks 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 System.Text;
using System.Text.RegularExpressions;
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 SpellBrigade.ModMenu;
using SpellBrigade.Shared;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.InputSystem;
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.1.2", "Relsev", null)]
[assembly: MelonGame("BoltBlasterGames", "TheSpellBrigade")]
[assembly: MelonOptionalDependencies(new string[] { "ModMenu" })]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("RunCustomizer")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.1.2.0")]
[assembly: AssemblyInformationalVersion("1.1.2+1856b18f4bf94ea33a9b586b39e9ee6f647e5453")]
[assembly: AssemblyProduct("RunCustomizer")]
[assembly: AssemblyTitle("RunCustomizer")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.1.2.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 SpellBrigade.Shared
{
	internal static class Hooks
	{
		public static bool Patch(Harmony harmony, Instance log, Type target, string method, Type patchClass, string prefix = null, string postfix = null)
		{
			return Patch(harmony, log, AccessTools.Method(target, method, (Type[])null, (Type[])null), patchClass, prefix, postfix, target.Name + "." + method);
		}

		public static bool Patch(Harmony harmony, Instance log, MethodBase original, Type patchClass, string prefix = null, string postfix = null, string name = null)
		{
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			if (name == null)
			{
				name = ((original != null) ? (original.DeclaringType?.Name + "." + original.Name) : ((prefix ?? postfix) + " target"));
			}
			try
			{
				if (original == null)
				{
					throw new MissingMethodException(name + " not found");
				}
				List<string> list = SharedCodeGuard.FindMethodsSharingCode(original);
				if (list.Count > 0)
				{
					log.Warning($"skipped {name}: its native code is shared with {list.Count} other method(s), patching would break them (e.g. {string.Join(", ", list.GetRange(0, Math.Min(5, list.Count)))})");
					return false;
				}
				harmony.Patch(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);
				return true;
			}
			catch (Exception ex)
			{
				log.Error($"failed to hook {name} — this part is disabled. {ex.GetType().Name}: {ex.Message}");
				return false;
			}
		}
	}
	internal sealed class Localizer
	{
		private readonly string[] _keys;

		private readonly Dictionary<string, string[]> _table;

		private readonly Func<Instance> _log;

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

		private readonly List<(TMP_Text text, Func<string> value)> _bound = new List<(TMP_Text, Func<string>)>();

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

		private string[] _current;

		private string _code;

		private TMP_Text _fontSample;

		private bool _subscribed;

		public Localizer(string[] keys, Dictionary<string, string[]> table, Func<Instance> log)
		{
			_keys = keys;
			_table = table;
			_log = log;
		}

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

		public void Bind(TMP_Text text, Func<string> value, TMP_Text fontSample = null)
		{
			if (!((Object)(object)text == (Object)null))
			{
				EnsureSubscribed();
				if (_current == null)
				{
					_current = Resolve(fontSample ?? text);
				}
				text.text = value();
				_bound.Add((text, value));
			}
		}

		public void Bind(TMP_Text text, string key, TMP_Text fontSample = null)
		{
			Bind(text, () => Get(key), fontSample);
		}

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

		public void UseFont(TMP_Text sample)
		{
			_fontSample = sample;
			_current = null;
			EnsureSubscribed();
		}

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

		private void Refresh()
		{
			_bound.RemoveAll(((TMP_Text text, Func<string> value) b) => (Object)(object)b.text == (Object)null);
			_current = Resolve((_bound.Count > 0) ? _bound[0].text : _fontSample);
			foreach (var (val, func) in _bound)
			{
				val.text = func();
			}
			foreach (Action listener in _listeners)
			{
				try
				{
					listener();
				}
				catch (Exception ex)
				{
					Instance obj = _log();
					if (obj != null)
					{
						obj.Warning("[lang] " + ex.Message);
					}
				}
			}
		}

		private static string LocaleCode()
		{
			try
			{
				Locale selectedLocale = LocalizationSettings.SelectedLocale;
				return ((selectedLocale != null) ? selectedLocale.Identifier.Code : null) ?? "en";
			}
			catch
			{
				return "en";
			}
		}

		private string[] Resolve(TMP_Text fontSample)
		{
			string text = (_code = LocaleCode());
			string text2 = TableKey(text);
			if (text2 != "en" && (Object)(object)fontSample != (Object)null && !FontHasAll(fontSample, text2))
			{
				Instance obj = _log();
				if (obj != null)
				{
					obj.Warning("[lang] font lacks glyphs for '" + text + "', using English");
				}
				text2 = "en";
			}
			return _table[text2];
		}

		private string TableKey(string code)
		{
			code = code.ToLowerInvariant().Replace('_', '-');
			string text = (code.StartsWith("zh") ? ((code.Contains("hant") || code.Contains("tw") || code.Contains("hk")) ? "zh-hant" : "zh-hans") : (code.StartsWith("pt") ? (code.Contains("br") ? "pt-br" : "pt") : ((!code.StartsWith("es")) ? code.Split('-')[0] : ((code == "es" || code.Contains("es-es")) ? "es" : "es-mx"))));
			if (_table.ContainsKey(text))
			{
				return text;
			}
			string text2 = text.Split('-')[0];
			if (!_table.ContainsKey(text2))
			{
				return "en";
			}
			return text2;
		}

		private bool FontHasAll(TMP_Text sample, string key)
		{
			if (_fontSupport.TryGetValue(key, out var value))
			{
				return value;
			}
			value = true;
			try
			{
				TMP_FontAsset font = sample.font;
				if ((Object)(object)font != (Object)null)
				{
					string[] array = _table[key];
					foreach (string text in array)
					{
						foreach (char c in text)
						{
							if (c > '\u007f' && !char.IsWhiteSpace(c) && !font.HasCharacter(c, true, true))
							{
								value = false;
								break;
							}
						}
						if (!value)
						{
							break;
						}
					}
				}
			}
			catch
			{
				value = true;
			}
			_fontSupport[key] = value;
			return value;
		}
	}
	internal static class MatchmakingGuard
	{
		private const string BlockersKey = "SpellBrigade.Matchmaking.Blockers";

		private const string OwnerKey = "SpellBrigade.Matchmaking.Owner";

		private const string PanelName = "SpellBrigade_MatchmakingLock";

		private static readonly Color Gold = new Color(0.96f, 0.78f, 0.35f);

		private static bool _owner;

		private static bool _wasBlocked;

		private static Instance _log;

		private static MatchmakingPreferencesModal _modal;

		private static TMP_Text _title;

		private static TMP_Text _body;

		private static float _pulse;

		private static float _nextCheck;

		private const string Title = "title";

		private const string Intro = "intro";

		private const string Footer = "footer";

		private static readonly Localizer Text = new Localizer(new string[3] { "title", "intro", "footer" }, new Dictionary<string, string[]>
		{
			["en"] = new string[3] { "Matchmaking is locked by mods", "These mods change the game for everyone in the lobby, so players without them can't be matched with you:", "Play with friends: invite them or share the lobby code. Matchmaking comes back as soon as nothing above applies." },
			["ru"] = new string[3] { "Подбор игроков заблокирован модами", "Эти моды меняют игру для всех в лобби, поэтому игроков без них к вам не подберёт:", "Играйте с друзьями: пригласите их или дайте код лобби. Подбор вернётся сам, как только ничего из списка не будет мешать." },
			["uk"] = new string[3] { "Підбір гравців заблоковано модами", "Ці моди змінюють гру для всіх у лобі, тому гравців без них до вас не підбере:", "Грайте з друзями: запросіть їх або дайте код лобі. Підбір повернеться сам, щойно нічого зі списку не заважатиме." },
			["de"] = new string[3] { "Matchmaking ist durch Mods gesperrt", "Diese Mods verändern das Spiel für alle in der Lobby, daher kannst du nicht mit Spielern ohne sie zusammengebracht werden:", "Spiele mit Freunden: Lade sie ein oder teile den Lobby-Code. Matchmaking kommt zurück, sobald nichts davon mehr zutrifft." },
			["fr"] = new string[3] { "Matchmaking bloqué par des mods", "Ces mods modifient la partie pour tout le salon, vous ne pouvez donc pas être associé à des joueurs qui ne les ont pas :", "Jouez avec des amis : invitez-les ou partagez le code du salon. Le matchmaking revient dès que plus rien ci-dessus ne s'applique." },
			["it"] = new string[3] { "Matchmaking bloccato dalle mod", "Queste mod cambiano la partita per tutta la lobby, quindi non puoi essere abbinato a chi non le ha:", "Gioca con gli amici: invitali o condividi il codice della lobby. Il matchmaking torna appena nulla di tutto ciò si applica." },
			["nl"] = new string[3] { "Matchmaking geblokkeerd door mods", "Deze mods veranderen het spel voor iedereen in de lobby, dus je wordt niet gekoppeld aan spelers zonder deze mods:", "Speel met vrienden: nodig ze uit of deel de lobbycode. Matchmaking komt terug zodra niets hiervan meer geldt." },
			["pl"] = new string[3] { "Dobieranie graczy zablokowane przez mody", "Te mody zmieniają grę wszystkim w lobby, więc gracze bez nich nie zostaną do ciebie dobrani:", "Graj ze znajomymi: zaproś ich lub podaj kod lobby. Dobieranie wróci samo, gdy nic z listy nie będzie przeszkadzać." },
			["pt-br"] = new string[3] { "Matchmaking bloqueado por mods", "Estes mods mudam o jogo para todos no lobby, então jogadores sem eles não serão pareados com você:", "Jogue com amigos: convide-os ou compartilhe o código do lobby. O matchmaking volta assim que nada acima se aplicar." },
			["pt"] = new string[3] { "Matchmaking bloqueado por mods", "Estes mods mudam o jogo para todos no lobby, por isso jogadores sem eles não serão emparelhados contigo:", "Joga com amigos: convida-os ou partilha o código do lobby. O matchmaking volta assim que nada acima se aplicar." },
			["es"] = new string[3] { "Emparejamiento bloqueado por mods", "Estos mods cambian la partida para toda la sala, así que no se te emparejará con jugadores que no los tengan:", "Juega con amigos: invítalos o comparte el código de sala. El emparejamiento vuelve en cuanto nada de lo anterior aplique." },
			["es-mx"] = new string[3] { "Emparejamiento bloqueado por mods", "Estos mods cambian la partida para toda la sala, así que no se te emparejará con jugadores que no los tengan:", "Juega con amigos: invítalos o comparte el código de sala. El emparejamiento regresa en cuanto nada de lo anterior aplique." },
			["ja"] = new string[3] { "MODによりマッチングがロックされています", "これらのMODはロビー全員のゲームを変えるため、MODのないプレイヤーとはマッチングしません:", "フレンドと遊びましょう:招待するかロビーコードを共有してください。上記が当てはまらなくなればマッチングは戻ります。" },
			["ko"] = new string[3] { "모드로 인해 매칭이 잠겼습니다", "이 모드들은 로비 전체의 게임을 바꾸므로, 모드가 없는 플레이어와는 매칭되지 않습니다:", "친구와 플레이하세요: 초대하거나 로비 코드를 공유하세요. 위 항목이 모두 해당되지 않으면 매칭이 다시 열립니다." },
			["zh-hans"] = new string[3] { "匹配已被模组锁定", "这些模组会改变大厅里所有人的游戏,因此不会为你匹配没有这些模组的玩家:", "和好友一起玩:邀请他们或分享大厅代码。只要上面各项都不再适用,匹配就会恢复。" },
			["zh-hant"] = new string[3] { "配對已被模組鎖定", "這些模組會改變大廳裡所有人的遊戲,因此不會為你配對沒有這些模組的玩家:", "和好友一起玩:邀請他們或分享大廳代碼。只要上面各項都不再適用,配對就會恢復。" },
			["th"] = new string[3] { "การจ\u0e31บค\u0e39\u0e48ถ\u0e39กล\u0e47อกโดยม\u0e47อด", "ม\u0e47อดเหล\u0e48าน\u0e35\u0e49เปล\u0e35\u0e48ยนเกมของท\u0e38กคนในล\u0e47อบบ\u0e35\u0e49 จ\u0e36งจะไม\u0e48จ\u0e31บค\u0e39\u0e48ค\u0e38ณก\u0e31บผ\u0e39\u0e49เล\u0e48นท\u0e35\u0e48ไม\u0e48ม\u0e35ม\u0e47อด:", "เล\u0e48นก\u0e31บเพ\u0e37\u0e48อน: เช\u0e34ญเพ\u0e37\u0e48อนหร\u0e37อแชร\u0e4cรห\u0e31สล\u0e47อบบ\u0e35\u0e49 การจ\u0e31บค\u0e39\u0e48จะกล\u0e31บมาเม\u0e37\u0e48อไม\u0e48ม\u0e35ข\u0e49อใดด\u0e49านบนเข\u0e49าเง\u0e37\u0e48อนไข" }
		}, () => _log);

		private static bool _prepareRestore;

		public static bool Blocked => Reasons().Count > 0;

		private static MatchmakingPreferencesManager Prefs => Singleton<MatchmakingPreferencesManager>.Instance;

		public static void Register(Harmony harmony, Instance log, string modName, Func<string> reason)
		{
			AppDomain currentDomain = AppDomain.CurrentDomain;
			List<Func<string>> list = currentDomain.GetData("SpellBrigade.Matchmaking.Blockers") as List<Func<string>>;
			if (list == null)
			{
				currentDomain.SetData("SpellBrigade.Matchmaking.Blockers", list = new List<Func<string>>());
			}
			list.Add(delegate
			{
				string text = reason();
				return (text == null) ? null : ("<b>" + modName + "</b> — " + text);
			});
			object data = currentDomain.GetData("SpellBrigade.Matchmaking.Owner");
			if (!(data is bool) || !(bool)data)
			{
				currentDomain.SetData("SpellBrigade.Matchmaking.Owner", true);
				_owner = true;
				_log = log;
				int num = 0;
				num += Hook(harmony, typeof(RunPreparationManager), "Prepare", "PreparePrefix", "PreparePostfix");
				num += Hook(harmony, typeof(RunPreparationManager), "CanUseMatchmaking", "CanUsePrefix");
				num += Hook(harmony, typeof(MatchmakingToggle), "Toggle", "TogglePrefix");
				num += Hook(harmony, typeof(MatchmakingPreferencesModal), "OnIsMatchmakingEnabledChanged", "EnabledChangedPrefix");
				num += Hook(harmony, typeof(MatchmakingPreferencesModal), "RefreshPanel", null, "RefreshPanelPostfix");
				log.Msg($"matchmaking guard: {num}/5 hooks");
			}
		}

		private static int Hook(Harmony harmony, Type type, string method, string prefix, string postfix = null)
		{
			return Hooks.Patch(harmony, _log, type, method, typeof(MatchmakingGuard), prefix, postfix) ? 1 : 0;
		}

		public static List<string> Reasons()
		{
			List<string> list = new List<string>();
			if (!(AppDomain.CurrentDomain.GetData("SpellBrigade.Matchmaking.Blockers") is List<Func<string>> list2))
			{
				return list;
			}
			foreach (Func<string> item in list2)
			{
				try
				{
					string text = item();
					if (text != null)
					{
						list.Add(text);
					}
				}
				catch
				{
				}
			}
			return list;
		}

		private static void PreparePrefix()
		{
			List<string> list = Reasons();
			MatchmakingPreferencesManager prefs = Prefs;
			if (list.Count != 0 && !((Object)(object)prefs == (Object)null) && prefs.IsMatchmakingEnabled)
			{
				prefs.IsMatchmakingEnabled = false;
				_prepareRestore = true;
				Instance log = _log;
				if (log != null)
				{
					log.Msg("run starts without matchmaking — " + StripTags(string.Join("; ", list)));
				}
			}
		}

		private static void PreparePostfix()
		{
			if (_prepareRestore)
			{
				_prepareRestore = false;
				MatchmakingPreferencesManager prefs = Prefs;
				if ((Object)(object)prefs != (Object)null)
				{
					prefs.IsMatchmakingEnabled = true;
				}
			}
		}

		private static bool CanUsePrefix(ref bool __result)
		{
			if (!Blocked)
			{
				return true;
			}
			__result = false;
			return false;
		}

		private static bool TogglePrefix(MatchmakingToggle __instance)
		{
			if (!Blocked || !IsMatchmakingToggle(__instance))
			{
				return true;
			}
			_pulse = 1f;
			return false;
		}

		private static bool EnabledChangedPrefix(MatchmakingPreferencesModal __instance, bool isOn)
		{
			if (!isOn || !Blocked)
			{
				return true;
			}
			RefreshPanelPostfix(__instance);
			return false;
		}

		private static bool IsMatchmakingToggle(MatchmakingToggle toggle)
		{
			if ((Object)(object)_modal != (Object)null && (Object)(object)_modal.isMatchmakingEnabledToggle != (Object)null)
			{
				return ((Il2CppObjectBase)_modal.isMatchmakingEnabledToggle).Pointer == ((Il2CppObjectBase)toggle).Pointer;
			}
			return false;
		}

		private static void RefreshPanelPostfix(MatchmakingPreferencesModal __instance)
		{
			try
			{
				_modal = __instance;
				Apply(__instance, Reasons());
			}
			catch (Exception ex)
			{
				Instance log = _log;
				if (log != null)
				{
					log.Warning("matchmaking window: " + ex.Message);
				}
			}
		}

		private static void Apply(MatchmakingPreferencesModal modal, List<string> reasons)
		{
			bool flag = reasons.Count > 0;
			MatchmakingToggle isMatchmakingEnabledToggle = modal.isMatchmakingEnabledToggle;
			if ((Object)(object)isMatchmakingEnabledToggle != (Object)null)
			{
				if (flag)
				{
					isMatchmakingEnabledToggle.isOn = false;
					isMatchmakingEnabledToggle.RefreshUI();
				}
				(((Component)isMatchmakingEnabledToggle).GetComponent<CanvasGroup>() ?? ((Component)isMatchmakingEnabledToggle).gameObject.AddComponent<CanvasGroup>()).alpha = (flag ? 0.4f : 1f);
			}
			if (flag && (Object)(object)modal.matchmakingPreferencesObject != (Object)null)
			{
				modal.matchmakingPreferencesObject.SetActive(false);
			}
			Transform val = ((Component)modal).transform.Find("SpellBrigade_MatchmakingLock");
			if ((Object)(object)val == (Object)null && flag)
			{
				val = BuildPanel(modal);
			}
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			((Component)val).gameObject.SetActive(flag);
			if (!flag)
			{
				return;
			}
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append(Text.Get("intro")).Append('\n');
			foreach (string reason in reasons)
			{
				stringBuilder.Append("\n•  ").Append(reason);
			}
			stringBuilder.Append("\n\n<alpha=#B3>").Append(Text.Get("footer"));
			_title.text = Text.Get("title");
			_body.text = stringBuilder.ToString();
		}

		private static Transform BuildPanel(MatchmakingPreferencesModal modal)
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
			Transform obj = ((Component)modal).transform.Find("Matchmaking_Description");
			TMP_Text val = ((obj != null) ? ((Component)obj).GetComponent<TMP_Text>() : null) ?? ((Component)modal).GetComponentInChildren<TMP_Text>(true);
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			RectTransform val2 = new GameObject("SpellBrigade_MatchmakingLock").AddComponent<RectTransform>();
			((Transform)val2).SetParent(((Component)modal).transform, false);
			Vector2 val3 = default(Vector2);
			((Vector2)(ref val3))..ctor(0f, 1f);
			val2.pivot = val3;
			Vector2 anchorMin = (val2.anchorMax = val3);
			val2.anchorMin = anchorMin;
			val2.anchoredPosition = new Vector2(250f, -1060f);
			val2.sizeDelta = new Vector2(2250f, 760f);
			float num = (val.enableAutoSizing ? val.fontSizeMax : val.fontSize);
			_title = CloneText(val, (Transform)(object)val2, "Title", num * 1.25f, Gold, (FontStyles)1);
			RectTransform rectTransform = _title.rectTransform;
			rectTransform.anchorMin = new Vector2(0f, 1f);
			rectTransform.anchorMax = new Vector2(1f, 1f);
			rectTransform.pivot = new Vector2(0f, 1f);
			rectTransform.sizeDelta = new Vector2(0f, num * 1.8f);
			rectTransform.anchoredPosition = Vector2.zero;
			_body = CloneText(val, (Transform)(object)val2, "Body", num, Color.white, (FontStyles)0);
			RectTransform rectTransform2 = _body.rectTransform;
			rectTransform2.anchorMin = Vector2.zero;
			rectTransform2.anchorMax = Vector2.one;
			rectTransform2.pivot = new Vector2(0f, 1f);
			rectTransform2.offsetMin = Vector2.zero;
			rectTransform2.offsetMax = new Vector2(0f, (0f - num) * 2f);
			_body.textWrappingMode = (TextWrappingModes)1;
			_body.enableAutoSizing = true;
			_body.fontSizeMin = num * 0.6f;
			_body.fontSizeMax = num;
			return (Transform)(object)val2;
		}

		private static TMP_Text CloneText(TMP_Text template, Transform parent, string name, float size, Color color, FontStyles style)
		{
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			TMP_Text component = Object.Instantiate<GameObject>(((Component)template).gameObject, parent).GetComponent<TMP_Text>();
			foreach (LocalizeStringEvent component2 in ((Component)component).GetComponents<LocalizeStringEvent>())
			{
				Object.Destroy((Object)(object)component2);
			}
			foreach (MonoBehaviour component3 in ((Component)component).GetComponents<MonoBehaviour>())
			{
				if ((Object)(object)component3 != (Object)null && (Object)(object)((Il2CppObjectBase)component3).TryCast<TMP_Text>() == (Object)null)
				{
					Object.Destroy((Object)(object)component3);
				}
			}
			((Object)component).name = name;
			component.enableAutoSizing = false;
			component.fontSize = size;
			((Graphic)component).color = color;
			component.fontStyle = style;
			component.alignment = (TextAlignmentOptions)257;
			component.textWrappingMode = (TextWrappingModes)0;
			component.overflowMode = (TextOverflowModes)0;
			((Graphic)component).raycastTarget = false;
			return component;
		}

		private static string StripTags(string s)
		{
			return Regex.Replace(s, "<.*?>", "");
		}

		public static void Tick()
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			if (!_owner)
			{
				return;
			}
			if (_pulse > 0f && (Object)(object)_title != (Object)null)
			{
				_pulse = Mathf.Max(0f, _pulse - Time.unscaledDeltaTime * 2.5f);
				_title.transform.localScale = Vector3.one * (1f + 0.08f * _pulse);
				((Graphic)_title).color = Color.Lerp(Gold, new Color(1f, 0.45f, 0.35f), _pulse);
			}
			if (Time.unscaledTime < _nextCheck)
			{
				return;
			}
			_nextCheck = Time.unscaledTime + 0.25f;
			List<string> list = Reasons();
			bool flag = list.Count > 0;
			if (flag != _wasBlocked)
			{
				_wasBlocked = flag;
				Instance log = _log;
				if (log != null)
				{
					log.Msg(flag ? ("matchmaking locked — " + StripTags(string.Join("; ", list))) : "matchmaking unlocked");
				}
				if ((Object)(object)_modal != (Object)null && ((Component)_modal).gameObject.activeInHierarchy)
				{
					try
					{
						_modal.RefreshPanel();
					}
					catch
					{
					}
				}
			}
			else if (flag && (Object)(object)_modal != (Object)null && ((Component)_modal).gameObject.activeInHierarchy && (Object)(object)_body != (Object)null)
			{
				Apply(_modal, list);
			}
		}
	}
	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 WheelSelect
	{
		private sealed class State
		{
			public ScrollRect Blocked;

			public float BlockedSensitivity;

			public Vector2 LastMouse = new Vector2(-1f, -1f);
		}

		private const string FrameKey = "SpellBrigade.WheelSelect.Frame";

		private const string CustomKey = "SpellBrigade.WheelSelect.Custom";

		private static readonly State Own = new State();

		private static List<(GameObject target, Action<int> step)> Custom
		{
			get
			{
				AppDomain currentDomain = AppDomain.CurrentDomain;
				if (currentDomain.GetData("SpellBrigade.WheelSelect.Custom") is List<(GameObject, Action<int>)> result)
				{
					return result;
				}
				List<(GameObject, Action<int>)> list = new List<(GameObject, Action<int>)>();
				currentDomain.SetData("SpellBrigade.WheelSelect.Custom", list);
				return list;
			}
		}

		public static void Register(GameObject target, Action<int> step)
		{
			if ((Object)(object)target != (Object)null && step != null)
			{
				Custom.Add((target, step));
			}
		}

		public static void Tick()
		{
			try
			{
				TickCore();
			}
			catch
			{
			}
		}

		private static void TickCore()
		{
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: 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_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			int frameCount = Time.frameCount;
			AppDomain currentDomain = AppDomain.CurrentDomain;
			if (currentDomain.GetData("SpellBrigade.WheelSelect.Frame") is int num && num == frameCount)
			{
				return;
			}
			currentDomain.SetData("SpellBrigade.WheelSelect.Frame", frameCount);
			object data = currentDomain.GetData("SpellBrigade.Modal");
			if (data is bool && (bool)data)
			{
				return;
			}
			Mouse current = Mouse.current;
			if (current == null)
			{
				return;
			}
			State own = Own;
			Vector2 val = ((InputControl<Vector2>)(object)((Pointer)current).position).ReadValue();
			float y = ((InputControl<Vector2>)(object)current.scroll).ReadValue().y;
			Vector2 val2 = val - own.LastMouse;
			if (!(((Vector2)(ref val2)).sqrMagnitude > 0.25f) && Mathf.Abs(y) < 0.01f)
			{
				return;
			}
			own.LastMouse = val;
			var (val3, action, val4) = Hovered(val);
			BlockScroll(own, ((Object)(object)val4 != (Object)null) ? ScrollRectAbove(val4) : null);
			if (Mathf.Abs(y) < 0.01f || (Object)(object)val4 == (Object)null)
			{
				return;
			}
			int num2 = ((y > 0f) ? 1 : (-1));
			try
			{
				if ((Object)(object)val3 != (Object)null)
				{
					if (num2 > 0)
					{
						val3.SelectNext();
					}
					else
					{
						val3.SelectPrevious();
					}
				}
				else
				{
					action?.Invoke(num2);
				}
			}
			catch
			{
			}
		}

		private static (Selector selector, Action<int> custom, RectTransform rect) Hovered(Vector2 position)
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			List<(GameObject target, Action<int> step)> custom = Custom;
			custom.RemoveAll(((GameObject target, Action<int> step) c) => (Object)(object)c.target == (Object)null);
			foreach (var (val, item) in custom)
			{
				if (val.activeInHierarchy && Contains(val.GetComponent<RectTransform>(), position))
				{
					return (selector: null, custom: item, rect: val.GetComponent<RectTransform>());
				}
			}
			foreach (Selector item2 in Object.FindObjectsOfType<Selector>())
			{
				if (!((Object)(object)item2 == (Object)null) && ((Selectable)item2).IsInteractable())
				{
					RectTransform component = ((Component)item2).GetComponent<RectTransform>();
					if (Contains(component, position))
					{
						return (selector: item2, custom: null, rect: component);
					}
				}
			}
			return (selector: null, custom: null, rect: null);
		}

		private static bool Contains(RectTransform rect, Vector2 position)
		{
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)rect == (Object)null)
			{
				return false;
			}
			Canvas val = null;
			Transform val2 = ((Component)rect).transform;
			while ((Object)(object)val2 != (Object)null)
			{
				CanvasGroup component = ((Component)val2).GetComponent<CanvasGroup>();
				if ((Object)(object)component != (Object)null && (component.alpha < 0.01f || !component.blocksRaycasts))
				{
					return false;
				}
				Canvas component2 = ((Component)val2).GetComponent<Canvas>();
				if ((Object)(object)component2 != (Object)null)
				{
					val = component2;
				}
				val2 = val2.parent;
			}
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			Camera val3 = (((int)val.renderMode == 0) ? null : val.worldCamera);
			if (!RectTransformUtility.RectangleContainsScreenPoint(rect, position, val3))
			{
				return false;
			}
			Transform parent = ((Transform)rect).parent;
			while ((Object)(object)parent != (Object)null)
			{
				if ((Object)(object)((Component)parent).GetComponent<RectMask2D>() != (Object)null && !RectTransformUtility.RectangleContainsScreenPoint(((Component)parent).GetComponent<RectTransform>(), position, val3))
				{
					return false;
				}
				parent = parent.parent;
			}
			return true;
		}

		private static ScrollRect ScrollRectAbove(RectTransform rect)
		{
			Transform parent = ((Transform)rect).parent;
			while ((Object)(object)parent != (Object)null)
			{
				ScrollRect component = ((Component)parent).GetComponent<ScrollRect>();
				if ((Object)(object)component != (Object)null)
				{
					return component;
				}
				parent = parent.parent;
			}
			return null;
		}

		private static void BlockScroll(State state, ScrollRect scroll)
		{
			if (!((Object)(object)state.Blocked != (Object)null) || !((Object)(object)scroll != (Object)null) || !(((Il2CppObjectBase)state.Blocked).Pointer == ((Il2CppObjectBase)scroll).Pointer))
			{
				if ((Object)(object)state.Blocked != (Object)null)
				{
					state.Blocked.scrollSensitivity = state.BlockedSensitivity;
				}
				state.Blocked = scroll;
				if (!((Object)(object)scroll == (Object)null))
				{
					state.BlockedSensitivity = scroll.scrollSensitivity;
					scroll.scrollSensitivity = 0f;
				}
			}
		}
	}
}
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);
				}
			}
		}
	}
	internal static class MenuPage
	{
		private static Mode RandomMode
		{
			get
			{
				if (!RunCustomizerMod.RandomBaseOnly.Value)
				{
					return Mode.Random;
				}
				return Mode.RandomBase;
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		public static void Register()
		{
			Menu.AddPage("RunCustomizer", "Run Customizer").Choice((Func<string>)(() => Strings.Get("difficulty")), (Func<IList<string>>)(() => new string[3]
			{
				Strings.Get("standard"),
				Strings.Get("rn"),
				Strings.Get("cn")
			}), (Func<int>)(() => Modes.Selected switch
			{
				Mode.Vanilla => 0, 
				Mode.Custom => 2, 
				_ => 1, 
			}), (Action<int>)delegate(int i)
			{
				Modes.Selected = i switch
				{
					0 => Mode.Vanilla, 
					2 => Mode.Custom, 
					_ => RandomMode, 
				};
			}, (int?)0).Choice((Func<string>)(() => Strings.Get("kind")), (Func<IList<string>>)(() => new string[2]
			{
				Strings.Get("kind_all"),
				Strings.Get("kind_base")
			}), (Func<int>)(() => RunCustomizerMod.RandomBaseOnly.Value ? 1 : 0), (Action<int>)delegate(int i)
			{
				bool flag = i == 1;
				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;
					}
				}
			}, (int?)(RunCustomizerMod.RandomBaseOnly.DefaultValue ? 1 : 0))
				.Header((Func<string>)(() => Strings.Get("cn")))
				.Number((Func<string>)(() => Strings.Get("enemy")), 0.1f, 10f, 0.05f, (Func<float>)(() => RunCustomizerMod.CustomEnemy.Value), (Action<float>)delegate(float v)
				{
					Set(RunCustomizerMod.CustomEnemy, v);
				}, (Func<float, string>)Multiplier, (float?)RunCustomizerMod.CustomEnemy.DefaultValue)
				.Number((Func<string>)(() => Strings.Get("spawn")), 0.1f, 10f, 0.05f, (Func<float>)(() => RunCustomizerMod.CustomSpawnSpeed.Value), (Action<float>)delegate(float v)
				{
					Set(RunCustomizerMod.CustomSpawnSpeed, v);
				}, (Func<float, string>)Multiplier, (float?)RunCustomizerMod.CustomSpawnSpeed.DefaultValue)
				.Number((Func<string>)(() => Strings.Get("count")), 0.1f, 10f, 0.05f, (Func<float>)(() => RunCustomizerMod.CustomEnemyCount.Value), (Action<float>)delegate(float v)
				{
					Set(RunCustomizerMod.CustomEnemyCount, v);
				}, (Func<float, string>)Multiplier, (float?)RunCustomizerMod.CustomEnemyCount.DefaultValue)
				.Number((Func<string>)(() => Strings.Get("health")), 0f, 10f, 0.05f, (Func<float>)(() => RunCustomizerMod.CustomHealth.Value), (Action<float>)delegate(float v)
				{
					Set(RunCustomizerMod.CustomHealth, v);
				}, (Func<float, string>)Multiplier, (float?)RunCustomizerMod.CustomHealth.DefaultValue);
		}

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

		private static void Set(MelonPreferences_Entry<float> entry, float value)
		{
			if (entry.Value != value)
			{
				entry.Value = value;
				RunCustomizerMod.Save();
			}
		}
	}
	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}");
			MatchmakingGuard.Register(((MelonBase)this).HarmonyInstance, Log, "Run Customizer", () => (Modes.Selected != Mode.Vanilla) ? string.Format(Strings.Get("mm"), Strings.Get((Modes.Selected == Mode.Custom) ? "cn" : "rn")) : null);
			if (MelonBase.FindMelon("Mod Menu", "Relsev") != null)
			{
				try
				{
					MenuPage.Register();
				}
				catch (Exception ex)
				{
					Log.Warning("Mod Menu page: " + ex.Message);
				}
			}
		}

		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()
		{
			WheelSelect.Tick();
			MatchmakingGuard.Tick();
			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)
		{
			_hooksTotal++;
			if (Hooks.Patch(((MelonBase)this).HarmonyInstance, Log, original, patchClass ?? typeof(DifficultyHooks), prefix, postfix))
			{
				_hooksInstalled++;
			}
		}
	}
	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;
						}
					}
				}
				CharacterButto