Decompiled source of GoldXpMultiplier v2.6.2

Mods/GoldXpMultiplier.dll

Decompiled 3 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
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 GoldXpMultiplier;
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 SpellBrigade.ModMenu;
using SpellBrigade.Shared;
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(GoldXpMod), "Gold & XP Multiplier", "2.6.2", "Relsev", null)]
[assembly: MelonGame("BoltBlasterGames", "TheSpellBrigade")]
[assembly: MelonOptionalDependencies(new string[] { "ModMenu" })]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("GoldXpMultiplier")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("2.6.2.0")]
[assembly: AssemblyInformationalVersion("2.6.2+1856b18f4bf94ea33a9b586b39e9ee6f647e5453")]
[assembly: AssemblyProduct("GoldXpMultiplier")]
[assembly: AssemblyTitle("GoldXpMultiplier")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("2.6.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 GoldXpMultiplier
{
	internal static class GoldPatch
	{
		public static void Postfix(EarnedGold __result)
		{
			if (!GoldXpMod.Enabled || __result == null)
			{
				return;
			}
			float mult = GoldXpMod.GoldMultiplier;
			if (mult != 1f)
			{
				int totalGold = __result.TotalGold;
				int[] array = Read(__result);
				Write(__result, Array.ConvertAll(array, (int v) => Scale(v, mult)));
				RunSummaryUi.RememberGold(__result, array, mult, totalGold);
			}
		}

		public static int[] Read(EarnedGold g)
		{
			return new int[6] { g._BaseGold_k__BackingField, g._BonusGoldFromFinalUpgrade_k__BackingField, g._BonusGoldFromGoldGainStat_k__BackingField, g._BonusGoldFromCurses_k__BackingField, g._BonusGoldFromDifficulty_k__BackingField, g._BonusGoldFromUnusedCharacter_k__BackingField };
		}

		private static void Write(EarnedGold g, int[] v)
		{
			g._BaseGold_k__BackingField = v[0];
			g._BonusGoldFromFinalUpgrade_k__BackingField = v[1];
			g._BonusGoldFromGoldGainStat_k__BackingField = v[2];
			g._BonusGoldFromCurses_k__BackingField = v[3];
			g._BonusGoldFromDifficulty_k__BackingField = v[4];
			g._BonusGoldFromUnusedCharacter_k__BackingField = v[5];
		}

		public static EarnedGold Create(int[] values, EarnedGold percentagesFrom)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Expected O, but got Unknown
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Expected O, but got Unknown
			EarnedGold val = new EarnedGold();
			Write(val, values);
			val._BonusGoldPercentageFromCurses_k__BackingField = percentagesFrom._BonusGoldPercentageFromCurses_k__BackingField;
			val._BonusGoldPercentageFromDifficulty_k__BackingField = percentagesFrom._BonusGoldPercentageFromDifficulty_k__BackingField;
			val._BonusGoldPercentageFromUnusedCharacter_k__BackingField = percentagesFrom._BonusGoldPercentageFromUnusedCharacter_k__BackingField;
			return val;
		}

		private static int Scale(int value, float mult)
		{
			if (value <= 0)
			{
				return value;
			}
			double num = Math.Round((double)value * (double)mult);
			if (!(num >= 2147483647.0))
			{
				return (int)num;
			}
			return int.MaxValue;
		}
	}
	internal static class XpPatch
	{
		public static void Prefix(PartyLevelManager __instance, ref float experience)
		{
			if (!(experience <= 0f))
			{
				float num = (GoldXpMod.Enabled ? GoldXpMod.XpMultiplier : 1f);
				float vanilla = experience;
				if (num != 1f)
				{
					experience *= num;
				}
				RunSummaryUi.TrackXp(__instance, vanilla, experience);
			}
		}
	}
	internal static class ImpactFx
	{
		private sealed class Spark
		{
			public RectTransform Rect;

			public Image Image;

			public Vector2 Velocity;

			public float Spin;
		}

		private const float FallDuration = 0.42f;

		private const float ImpactDuration = 0.55f;

		private static readonly Color SparkColor = new Color(1f, 0.84f, 0.3f, 1f);

		private static Sprite _round;

		public static IEnumerator Slam(TMP_Text target, TMP_Text style, string label, string newText, Transform shakeRoot, Action onImpact)
		{
			if ((Object)(object)target == (Object)null)
			{
				yield break;
			}
			object obj;
			if (!((Object)(object)((Graphic)target).canvas != (Object)null))
			{
				obj = null;
			}
			else
			{
				Canvas rootCanvas = ((Graphic)target).canvas.rootCanvas;
				obj = ((rootCanvas != null) ? ((Component)rootCanvas).GetComponent<RectTransform>() : null);
			}
			RectTransform root = (RectTransform)obj;
			if ((Object)(object)root == (Object)null)
			{
				target.text = newText;
				onImpact?.Invoke();
				yield break;
			}
			target.ForceMeshUpdate(false, false);
			Transform transform = target.transform;
			Bounds textBounds = target.textBounds;
			Vector3 end = ((Transform)root).InverseTransformPoint(transform.TransformPoint(((Bounds)(ref textBounds)).center));
			Rect rect = root.rect;
			Vector3 start = end + new Vector3(0f, ((Rect)(ref rect)).height * 0.6f, 0f);
			TMP_Text slam = CreateLabel(root, style ?? target, label);
			RectTransform slamRect = slam.rectTransform;
			float t0 = Time.unscaledTime;
			while (true)
			{
				if ((Object)(object)target == (Object)null || (Object)(object)slam == (Object)null)
				{
					Cleanup(slam, null);
					yield break;
				}
				float num = Mathf.Clamp01((Time.unscaledTime - t0) / 0.42f);
				float num2 = num * num * num;
				((Transform)slamRect).localPosition = Vector3.LerpUnclamped(start, end, num2);
				float num3 = Mathf.Lerp(2.4f, 1f, num2);
				((Transform)slamRect).localScale = new Vector3(num3 * 0.85f, num3 * 1.15f, 1f);
				((Transform)slamRect).localRotation = Quaternion.Euler(0f, 0f, Mathf.Lerp(-16f, 0f, num2));
				slam.alpha = Mathf.Clamp01(num * 5f);
				if (num >= 1f)
				{
					break;
				}
				yield return null;
			}
			target.text = newText;
			try
			{
				onImpact?.Invoke();
			}
			catch (Exception ex)
			{
				GoldXpMod.Log.Warning("[fx] " + ex.Message);
			}
			List<Spark> sparks = SpawnSparks(root, end, 26);
			Image flash = SpawnFlash(root, end);
			Vector3 targetScale = target.transform.localScale;
			Vector3 shakeBase = (((Object)(object)shakeRoot != (Object)null) ? shakeRoot.localPosition : Vector3.zero);
			t0 = Time.unscaledTime;
			float last = t0;
			while (true)
			{
				float unscaledTime = Time.unscaledTime;
				float num4 = unscaledTime - last;
				last = unscaledTime;
				float num5 = Mathf.Clamp01((unscaledTime - t0) / 0.55f);
				if ((Object)(object)target != (Object)null)
				{
					target.transform.localScale = targetScale * (1f + 0.55f * Punch(num5));
				}
				if ((Object)(object)shakeRoot != (Object)null)
				{
					float num6 = 22f * (1f - num5) * (1f - num5);
					shakeRoot.localPosition = shakeBase + new Vector3(Random.Range(0f - num6, num6), Random.Range(0f - num6, num6), 0f);
				}
				if ((Object)(object)slam != (Object)null)
				{
					float num7 = Mathf.Exp(-9f * num5);
					((Transform)slamRect).localScale = new Vector3(1f + 0.6f * num7 + 0.25f * num5, 1f - 0.45f * num7 + 0.25f * num5, 1f);
					((Transform)slamRect).localPosition = end + new Vector3(0f, 70f * Mathf.Sin(num5 * (float)Math.PI * 0.5f), 0f);
					slam.alpha = 1f - num5 * num5;
				}
				if ((Object)(object)flash != (Object)null)
				{
					float num8 = 1f - Mathf.Pow(1f - num5, 3f);
					((Transform)((Graphic)flash).rectTransform).localScale = Vector3.one * Mathf.Lerp(0.2f, 3.2f, num8);
					Color color = ((Graphic)flash).color;
					color.a = 0.8f * (1f - num8);
					((Graphic)flash).color = color;
				}
				foreach (Spark item in sparks)
				{
					if (!((Object)(object)item.Rect == (Object)null))
					{
						item.Velocity += new Vector2(0f, -1800f) * num4;
						RectTransform rect2 = item.Rect;
						((Transform)rect2).localPosition = ((Transform)rect2).localPosition + Vector2.op_Implicit(item.Velocity * num4);
						((Transform)item.Rect).localRotation = Quaternion.Euler(0f, 0f, ((Transform)item.Rect).localEulerAngles.z + item.Spin * num4);
						Color color2 = ((Graphic)item.Image).color;
						color2.a = 1f - num5;
						((Graphic)item.Image).color = color2;
						((Transform)item.Rect).localScale = Vector3.one * (1f - 0.6f * num5);
					}
				}
				if (num5 >= 1f)
				{
					break;
				}
				yield return null;
			}
			if ((Object)(object)target != (Object)null)
			{
				target.transform.localScale = targetScale;
			}
			if ((Object)(object)shakeRoot != (Object)null)
			{
				shakeRoot.localPosition = shakeBase;
			}
			Cleanup(slam, sparks);
			if ((Object)(object)flash != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)flash).gameObject);
			}
		}

		private static float Punch(float k)
		{
			return Mathf.Exp(-6f * k) * Mathf.Sin(k * (float)Math.PI * 3.5f + 0.35f) * ((k < 0.02f) ? (k / 0.02f) : 1f);
		}

		private static TMP_Text CreateLabel(RectTransform root, TMP_Text style, string label)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("SBMod_Slam");
			val.AddComponent<RectTransform>();
			val.transform.SetParent((Transform)(object)root, false);
			val.transform.SetAsLastSibling();
			TextMeshProUGUI obj = val.AddComponent<TextMeshProUGUI>();
			((TMP_Text)obj).font = style.font;
			((TMP_Text)obj).fontSharedMaterial = style.fontSharedMaterial;
			((TMP_Text)obj).fontStyle = style.fontStyle;
			((Graphic)obj).color = ((Graphic)style).color;
			((TMP_Text)obj).enableVertexGradient = style.enableVertexGradient;
			((TMP_Text)obj).colorGradient = style.colorGradient;
			((TMP_Text)obj).fontSize = style.fontSize * 1.5f;
			((TMP_Text)obj).alignment = (TextAlignmentOptions)514;
			((TMP_Text)obj).textWrappingMode = (TextWrappingModes)0;
			((Graphic)obj).raycastTarget = false;
			((TMP_Text)obj).text = label;
			((TMP_Text)obj).rectTransform.sizeDelta = new Vector2(800f, 400f);
			return (TMP_Text)(object)obj;
		}

		private static List<Spark> SpawnSparks(RectTransform root, Vector3 at, int count)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			List<Spark> list = new List<Spark>();
			for (int i = 0; i < count; i++)
			{
				GameObject val = new GameObject("SBMod_Spark");
				RectTransform val2 = val.AddComponent<RectTransform>();
				val.transform.SetParent((Transform)(object)root, false);
				val.transform.SetAsLastSibling();
				Image val3 = val.AddComponent<Image>();
				val3.sprite = RoundSprite();
				((Graphic)val3).color = ((i % 3 == 0) ? Color.white : SparkColor);
				((Graphic)val3).raycastTarget = false;
				float num = Random.Range(22f, 48f);
				val2.sizeDelta = new Vector2(num, num);
				((Transform)val2).localPosition = at;
				((Transform)val2).localRotation = Quaternion.Euler(0f, 0f, 45f);
				float num2 = Random.Range(10f, 170f) * ((float)Math.PI / 180f);
				float num3 = Random.Range(500f, 1100f);
				list.Add(new Spark
				{
					Rect = val2,
					Image = val3,
					Velocity = new Vector2(Mathf.Cos(num2), Mathf.Sin(num2)) * num3,
					Spin = Random.Range(-720f, 720f)
				});
			}
			return list;
		}

		private static Image SpawnFlash(RectTransform root, Vector3 at)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			Sprite val = RoundSprite();
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			GameObject val2 = new GameObject("SBMod_Flash");
			RectTransform val3 = val2.AddComponent<RectTransform>();
			val2.transform.SetParent((Transform)(object)root, false);
			val2.transform.SetAsLastSibling();
			Image obj = val2.AddComponent<Image>();
			obj.sprite = val;
			((Graphic)obj).color = new Color(1f, 0.9f, 0.55f, 0.8f);
			((Graphic)obj).raycastTarget = false;
			val3.sizeDelta = new Vector2(320f, 320f);
			((Transform)val3).localPosition = at;
			return obj;
		}

		private static Sprite RoundSprite()
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Expected O, but got Unknown
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_round != (Object)null)
			{
				return _round;
			}
			try
			{
				Texture2D val = new Texture2D(64, 64, (TextureFormat)4, false);
				Il2CppStructArray<Color32> val2 = new Il2CppStructArray<Color32>(4096L);
				float num = 31.5f;
				for (int i = 0; i < 64; i++)
				{
					for (int j = 0; j < 64; j++)
					{
						float num2 = Mathf.Sqrt(((float)j - num) * ((float)j - num) + ((float)i - num) * ((float)i - num)) / num;
						float num3 = Mathf.Clamp01(1f - num2);
						num3 = num3 * num3 * (3f - 2f * num3);
						((Il2CppArrayBase<Color32>)(object)val2)[i * 64 + j] = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, (byte)(num3 * 255f));
					}
				}
				val.SetPixels32(val2);
				val.Apply(false, true);
				((Object)val).hideFlags = (HideFlags)61;
				_round = Sprite.Create(val, new Rect(0f, 0f, 64f, 64f), new Vector2(0.5f, 0.5f));
				((Object)_round).hideFlags = (HideFlags)61;
			}
			catch (Exception ex)
			{
				GoldXpMod.Log.Warning("[fx] no round sprite: " + ex.Message);
			}
			return _round;
		}

		private static void Cleanup(TMP_Text slam, List<Spark> sparks)
		{
			if ((Object)(object)slam != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)slam).gameObject);
			}
			if (sparks == null)
			{
				return;
			}
			foreach (Spark spark in sparks)
			{
				if ((Object)(object)spark.Rect != (Object)null)
				{
					Object.Destroy((Object)(object)((Component)spark.Rect).gameObject);
				}
			}
		}
	}
	internal static class MenuPage
	{
		[MethodImpl(MethodImplOptions.NoInlining)]
		public static void Register()
		{
			Menu.AddPage("GoldMultiplier", "Gold & XP Multiplier").Toggle((Func<string>)(() => Strings.Get("enabled")), (Func<bool>)(() => GoldXpMod._enabledEntry.Value), (Action<bool>)delegate(bool v)
			{
				Store(GoldXpMod._enabledEntry, v);
			}, (bool?)GoldXpMod._enabledEntry.DefaultValue).Number((Func<string>)(() => Strings.Get("gold")), (IEnumerable<float>)SettingsTab.Presets, (Func<float>)(() => GoldXpMod._goldEntry.Value), (Action<float>)delegate(float v)
			{
				Store(GoldXpMod._goldEntry, v);
			}, (Func<float, string>)Format, (float?)GoldXpMod._goldEntry.DefaultValue)
				.Number((Func<string>)(() => Strings.Get("xp")), (IEnumerable<float>)SettingsTab.Presets, (Func<float>)(() => GoldXpMod._xpEntry.Value), (Action<float>)delegate(float v)
				{
					Store(GoldXpMod._xpEntry, v);
				}, (Func<float, string>)Format, (float?)GoldXpMod._xpEntry.DefaultValue)
				.Number((Func<string>)(() => Strings.Get("rank")), (IEnumerable<float>)SettingsTab.Presets, (Func<float>)(() => GoldXpMod._rankXpEntry.Value), (Action<float>)delegate(float v)
				{
					Store(GoldXpMod._rankXpEntry, v);
				}, (Func<float, string>)Format, (float?)GoldXpMod._rankXpEntry.DefaultValue)
				.Toggle((Func<string>)(() => Strings.Get("reveal")), (Func<bool>)(() => GoldXpMod._revealEntry.Value), (Action<bool>)delegate(bool v)
				{
					Store(GoldXpMod._revealEntry, v);
				}, (bool?)GoldXpMod._revealEntry.DefaultValue)
				.Note((Func<string>)(() => Strings.Get("hint")));
		}

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

		private static void Store<T>(MelonPreferences_Entry<T> entry, T value)
		{
			if (!object.Equals(entry.Value, value))
			{
				entry.Value = value;
				GoldXpMod.SaveSettings();
			}
		}
	}
	public class GoldXpMod : MelonMod
	{
		public static Instance Log;

		private static MelonPreferences_Category _category;

		internal static MelonPreferences_Entry<bool> _enabledEntry;

		internal static MelonPreferences_Entry<float> _goldEntry;

		internal static MelonPreferences_Entry<float> _xpEntry;

		internal static MelonPreferences_Entry<float> _rankXpEntry;

		internal static MelonPreferences_Entry<bool> _revealEntry;

		private int _hooksInstalled;

		private int _hooksTotal;

		public static bool Enabled => _enabledEntry?.Value ?? true;

		public static float GoldMultiplier => Sanitize(_goldEntry?.Value);

		public static float XpMultiplier => Sanitize(_xpEntry?.Value);

		public static float RankXpMultiplier => Sanitize(_rankXpEntry?.Value);

		public static bool Reveal => _revealEntry?.Value ?? true;

		public override void OnInitializeMelon()
		{
			Log = ((MelonBase)this).LoggerInstance;
			ValueRange<float> val = new ValueRange<float>(0f, 1000f);
			_category = MelonPreferences.CreateCategory("GoldMultiplier", "Gold & XP Multiplier");
			_enabledEntry = _category.CreateEntry<bool>("Enabled", true, "Enabled", "Включён ли мод", false, false, (ValueValidator)null, (string)null);
			_goldEntry = _category.CreateEntry<float>("Multiplier", 2f, "Gold Multiplier", "Множитель золота за забег (1.0 = ваниль). Умножается и база, и все бонусы", false, false, (ValueValidator)(object)val, (string)null);
			_xpEntry = _category.CreateEntry<float>("XpMultiplier", 2f, "Run XP Multiplier", "Множитель опыта в забеге (1.0 = ваниль). В коопе работает, если мод стоит у хоста", false, false, (ValueValidator)(object)val, (string)null);
			_rankXpEntry = _category.CreateEntry<float>("RankXpMultiplier", 1f, "Wizard Rank XP Multiplier", "Множитель опыта ранга мага за забег (1.0 = ваниль)", false, false, (ValueValidator)(object)val, (string)null);
			_revealEntry = _category.CreateEntry<bool>("Reveal", true, "End-of-run x2 Reveal", "Экран итогов: сначала ванильные числа, потом удар множителя. false — сразу итоговые числа", false, false, (ValueValidator)null, (string)null);
			_category.SaveToFile(false);
			WarnAboutOldVersion();
			ApplyPatches();
			MatchmakingGuard.Register(((MelonBase)this).HarmonyInstance, Log, "Gold & XP Multiplier", () => (!Enabled || XpMultiplier == 1f) ? null : string.Format(Strings.Get("mm"), "x" + XpMultiplier.ToString("0.##", CultureInfo.InvariantCulture)));
			Log.Msg($"{_hooksInstalled}/{_hooksTotal} hooks — gold x{GoldMultiplier}, run xp x{XpMultiplier}, rank xp x{RankXpMultiplier}{(Enabled ? "" : " (disabled)")}. Settings: Options → Multipliers");
		}

		public override void OnSceneWasInitialized(int buildIndex, string sceneName)
		{
			if (sceneName == "StartMenu")
			{
				MelonCoroutines.Start(RepairSaveLater());
			}
		}

		private static IEnumerator RepairSaveLater()
		{
			yield return (object)new WaitForSeconds(2f);
			try
			{
				SaveDoctor.RepairCorruptedRanks();
			}
			catch (Exception value)
			{
				Log.Error($"rank repair failed: {value}");
			}
		}

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

		public override void OnUpdate()
		{
			WheelSelect.Tick();
			MatchmakingGuard.Tick();
		}

		private static float Sanitize(float? value)
		{
			float num = value ?? 1f;
			if (!float.IsNaN(num) && !float.IsInfinity(num) && !(num < 0f))
			{
				return num;
			}
			return 1f;
		}

		private static void WarnAboutOldVersion()
		{
			if (MelonBase.RegisteredMelons.Any((MelonBase m) => m.Info.Name == "SpellBrigadeGoldMultiplier"))
			{
				Log.Warning("Old version found: delete Mods\\SpellBrigadeGoldMultiplier.dll, otherwise rewards are multiplied twice");
			}
		}

		private void ApplyPatches()
		{
			Patch(typeof(RunGoldCalculator), "Calculate", typeof(GoldPatch), null, "Postfix");
			Patch(typeof(PartyLevelManager), "TryAddExperience", typeof(XpPatch), "Prefix");
			Patch(typeof(CharacterRankProgression), "SetRankProgression", typeof(RankXp), "SetRankProgressionPrefix");
			Patch(typeof(RunRecordFactory), "CalculateRankProgression", typeof(RankXp), null, "CalculateRankProgressionPostfix");
			Patch(typeof(GameOverMissionStatsPanel), "SetValues", typeof(RunSummaryUi), null, "SetValuesPostfix");
			Patch(typeof(GameOverMissionStatsPanel), "SetXPEarned", typeof(RunSummaryUi), "SetXpEarnedPrefix");
			bool flag = false;
			if (MelonBase.FindMelon("Mod Menu", "Relsev") != null)
			{
				try
				{
					MenuPage.Register();
					flag = true;
				}
				catch (Exception ex)
				{
					Log.Warning("Mod Menu page: " + ex.Message + " — using own settings tab");
				}
			}
			if (!flag)
			{
				Patch(typeof(SettingsPanel), "SetupSubPanels", typeof(SettingsTab), "InjectPrefix", "LinkTabButtonsPostfix");
			}
		}

		private void Patch(Type target, string method, Type patchClass, string prefix = null, string postfix = null)
		{
			_hooksTotal++;
			string text = target.Name + "." + method;
			MethodInfo methodInfo = AccessTools.Method(target, method, (Type[])null, (Type[])null);
			if (methodInfo != null && ReturnsStruct(methodInfo))
			{
				Log.Warning($"skipped {text}: it returns a struct ({methodInfo.ReturnType.Name}), hooking it corrupts the result");
			}
			else if (Hooks.Patch(((MelonBase)this).HarmonyInstance, Log, methodInfo, patchClass, prefix, postfix, text))
			{
				_hooksInstalled++;
			}
		}

		private static bool ReturnsStruct(MethodInfo method)
		{
			Type returnType = method.ReturnType;
			if (returnType == typeof(void) || returnType.IsPrimitive || returnType.IsEnum)
			{
				return false;
			}
			if (!returnType.IsValueType)
			{
				return typeof(ValueType).IsAssignableFrom(returnType);
			}
			return true;
		}
	}
	internal static class RankXp
	{
		[ThreadStatic]
		public static bool Suspended;

		private static (int r0, float p0, int r, float p) _lastScaled = (r0: -1, p0: 0f, r: -1, p: 0f);

		public static void SetRankProgressionPrefix(CharacterRankProgression __instance, CharacterId characterId, ref int rank, ref float rankProgress)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (Suspended || !GoldXpMod.Enabled)
				{
					return;
				}
				float rankXpMultiplier = GoldXpMod.RankXpMultiplier;
				if (rankXpMultiplier == 1f)
				{
					return;
				}
				CharacterRankProgress orCreateProgressForCharacter = __instance.GetOrCreateProgressForCharacter(characterId);
				if (orCreateProgressForCharacter != null)
				{
					int currentRank = orCreateProgressForCharacter.CurrentRank;
					float progressTowardsNextRank = orCreateProgressForCharacter.ProgressTowardsNextRank;
					if ((_lastScaled.r0 != currentRank || _lastScaled.p0 != progressTowardsNextRank || _lastScaled.r != rank || _lastScaled.p != rankProgress) && TryScale(__instance.rankCalculator, orCreateProgressForCharacter.Prestige, currentRank, progressTowardsNextRank, rank, rankProgress, rankXpMultiplier, out var rank2, out var progress))
					{
						rank = rank2;
						rankProgress = progress;
					}
				}
			}
			catch (Exception value)
			{
				GoldXpMod.Log.Error($"[rank xp] {value}");
			}
		}

		public static void CalculateRankProgressionPostfix(RankProgression __result, int prestige)
		{
			try
			{
				if (__result == (RankProgression)null || !GoldXpMod.Enabled)
				{
					return;
				}
				float rankXpMultiplier = GoldXpMod.RankXpMultiplier;
				if (rankXpMultiplier != 1f)
				{
					LocalPlayer instance = SingletonPersistent<LocalPlayer>.Instance;
					object calc;
					if (instance == null)
					{
						calc = null;
					}
					else
					{
						CharacterRankProgression characterRankManager = instance.GetCharacterRankManager();
						calc = ((characterRankManager != null) ? characterRankManager.rankCalculator : null);
					}
					int startRank = __result.StartRank;
					int endRank = __result.EndRank;
					float startRankProgress = __result.StartRankProgress;
					float endRankProgress = __result.EndRankProgress;
					if (TryScale((CharacterRankCalculator)calc, prestige, startRank, startRankProgress, endRank, endRankProgress, rankXpMultiplier, out var rank, out var progress))
					{
						__result._EndRank_k__BackingField = rank;
						__result._EndRankProgress_k__BackingField = progress;
						_lastScaled = (r0: startRank, p0: startRankProgress, r: rank, p: progress);
					}
				}
			}
			catch (Exception value)
			{
				GoldXpMod.Log.Error($"[rank xp] {value}");
			}
		}

		private static bool TryScale(CharacterRankCalculator calc, int prestige, int r0, float p0, int r1, float p1, float mult, out int rank, out float progress)
		{
			rank = r1;
			progress = p1;
			if (calc == null || mult == 1f)
			{
				return false;
			}
			if (!Sane(r0, p0) || !Sane(r1, p1))
			{
				return false;
			}
			if (r1 < r0 || (r1 == r0 && p1 <= p0))
			{
				return false;
			}
			if (r1 - r0 > 1000 || calc.IsMaxRank(r0) || calc.IsMaxRank(r1))
			{
				return false;
			}
			double num;
			if (r1 == r0)
			{
				num = (double)(p1 - p0) * Minutes(calc, r0, prestige);
			}
			else
			{
				num = (double)(1f - p0) * Minutes(calc, r0, prestige);
				for (int i = r0 + 1; i < r1; i++)
				{
					num += Minutes(calc, i, prestige);
				}
				num += (double)p1 * Minutes(calc, r1, prestige);
			}
			if (!(num > 0.0) || double.IsInfinity(num))
			{
				return false;
			}
			double num2 = num * (double)mult;
			int num3 = r0;
			double num4 = p0;
			while (true)
			{
				if (calc.IsMaxRank(num3))
				{
					num4 = 0.0;
					break;
				}
				double num5 = Minutes(calc, num3, prestige);
				if (!(num5 > 0.0) || double.IsInfinity(num5))
				{
					return false;
				}
				double num6 = (1.0 - num4) * num5;
				if (num2 < num6)
				{
					num4 += num2 / num5;
					break;
				}
				num2 -= num6;
				num3++;
				num4 = 0.0;
				if (num3 - r0 > 10000)
				{
					return false;
				}
			}
			rank = num3;
			progress = (float)Math.Min(num4, 0.9999);
			return Sane(rank, progress);
		}

		private static double Minutes(CharacterRankCalculator calc, int rank, int prestige)
		{
			return calc.CalculateMinutesRequiredForNextRank(rank, prestige);
		}

		private static bool Sane(int rank, float progress)
		{
			if (rank >= 0 && rank <= 1000 && !float.IsNaN(progress) && progress >= 0f)
			{
				return progress <= 1f;
			}
			return false;
		}
	}
	internal static class RunSummaryUi
	{
		private sealed class GoldSnapshot
		{
			public int[] Vanilla;

			public int[] Modded;

			public float Mult;

			public int VanillaTotal;

			public int ModdedTotal;
		}

		private sealed class Pending
		{
			public EarnedGoldUI GoldUi;

			public EarnedGold ModdedGold;

			public GoldSnapshot Gold;

			public TMP_Text XpText;

			public float XpVanilla;

			public float XpModded;

			public float XpMult;

			public Transform ShakeRoot;
		}

		private const string BonusPanelName = "SBMod_GoldBonus";

		private static readonly Dictionary<IntPtr, GoldSnapshot> Snapshots = new Dictionary<IntPtr, GoldSnapshot>();

		private static readonly List<GoldSnapshot> RecentSnapshots = new List<GoldSnapshot>();

		private static IntPtr _xpRunId;

		private static double _xpVanilla;

		private static double _xpModded;

		private static Pending _pending;

		private static bool _sequenceQueued;

		public static void RememberGold(EarnedGold modded, int[] vanilla, float mult, int vanillaTotal)
		{
			GoldSnapshot goldSnapshot = new GoldSnapshot
			{
				Vanilla = vanilla,
				Modded = GoldPatch.Read(modded),
				Mult = mult,
				VanillaTotal = vanillaTotal,
				ModdedTotal = modded.TotalGold
			};
			if (Snapshots.Count > 64)
			{
				Snapshots.Clear();
			}
			Snapshots[((Il2CppObjectBase)modded).Pointer] = goldSnapshot;
			RecentSnapshots.Add(goldSnapshot);
			if (RecentSnapshots.Count > 16)
			{
				RecentSnapshots.RemoveAt(0);
			}
		}

		private static GoldSnapshot FindSnapshot(EarnedGold gold)
		{
			if (Snapshots.TryGetValue(((Il2CppObjectBase)gold).Pointer, out var value))
			{
				return value;
			}
			int[] b = GoldPatch.Read(gold);
			for (int num = RecentSnapshots.Count - 1; num >= 0; num--)
			{
				if (Same(RecentSnapshots[num].Modded, b))
				{
					return RecentSnapshots[num];
				}
			}
			return null;
		}

		private static bool Same(int[] a, int[] b)
		{
			for (int i = 0; i < a.Length; i++)
			{
				if (a[i] != b[i])
				{
					return false;
				}
			}
			return true;
		}

		public static void TrackXp(PartyLevelManager manager, float vanilla, float modded)
		{
			if (!((Object)(object)manager == (Object)null))
			{
				if (((Il2CppObjectBase)manager).Pointer != _xpRunId)
				{
					_xpRunId = ((Il2CppObjectBase)manager).Pointer;
					_xpVanilla = 0.0;
					_xpModded = 0.0;
				}
				_xpVanilla += vanilla;
				_xpModded += modded;
			}
		}

		public static void SetValuesPostfix(GameOverMissionStatsPanel __instance)
		{
			try
			{
				if (!GoldXpMod.Reveal)
				{
					return;
				}
				EarnedGoldUI earnedGoldUI = __instance.earnedGoldUI;
				EarnedGold val = ((earnedGoldUI != null) ? earnedGoldUI.earnedGold : null);
				if (val != null)
				{
					GoldSnapshot goldSnapshot = FindSnapshot(val);
					if (goldSnapshot != null && goldSnapshot.Mult != 1f && goldSnapshot.VanillaTotal != goldSnapshot.ModdedTotal)
					{
						Queue().GoldUi = earnedGoldUI;
						_pending.ShakeRoot = ((Component)__instance).transform;
						_pending.ModdedGold = val;
						_pending.Gold = goldSnapshot;
						earnedGoldUI.earnedGold = GoldPatch.Create(goldSnapshot.Vanilla, val);
					}
				}
			}
			catch (Exception value)
			{
				GoldXpMod.Log.Error($"[summary] gold: {value}");
			}
		}

		public static void SetXpEarnedPrefix(GameOverMissionStatsPanel __instance, ref float xpEarned)
		{
			try
			{
				if (GoldXpMod.Reveal && _xpModded > 0.0 && _xpVanilla > 0.0 && !(Math.Abs(_xpModded - _xpVanilla) < 0.5))
				{
					float num = (float)((double)xpEarned * (_xpVanilla / _xpModded));
					if (Math.Round(num) != Math.Round(xpEarned))
					{
						Queue().XpText = __instance.xpEarnedValue;
						_pending.ShakeRoot = ((Component)__instance).transform;
						_pending.XpMult = GoldXpMod.XpMultiplier;
						_pending.XpVanilla = num;
						_pending.XpModded = xpEarned;
						xpEarned = num;
					}
				}
			}
			catch (Exception value)
			{
				GoldXpMod.Log.Error($"[summary] xp: {value}");
			}
		}

		private static Pending Queue()
		{
			if (_pending == null)
			{
				_pending = new Pending();
			}
			if (!_sequenceQueued)
			{
				_sequenceQueued = true;
				MelonCoroutines.Start(Sequence());
			}
			return _pending;
		}

		private static IEnumerator Sequence()
		{
			yield return null;
			Pending job = _pending;
			_pending = null;
			_sequenceQueued = false;
			if (job == null)
			{
				yield break;
			}
			TMP_Text goldText = (((Object)(object)job.GoldUi != (Object)null) ? job.GoldUi.totalGoldEarnedText : null);
			if ((Object)(object)goldText != (Object)null && job.Gold != null)
			{
				yield return WaitForValue(goldText, job.Gold.VanillaTotal);
				EarnedGoldUI ui = job.GoldUi;
				yield return ImpactFx.Slam(goldText, goldText, FormatMult(job.Gold.Mult), FormatLike(goldText.text, job.Gold.ModdedTotal), job.ShakeRoot, delegate
				{
					ShowBonusPanel(ui, job.Gold.Mult);
					PlayBonusSound(ui);
				});
				if ((Object)(object)ui != (Object)null && job.ModdedGold != null)
				{
					ui.earnedGold = job.ModdedGold;
				}
			}
			if ((Object)(object)job.XpText != (Object)null)
			{
				yield return WaitVisible(job.XpText);
				string label = FormatMult(job.XpMult);
				yield return ImpactFx.Slam(job.XpText, goldText ?? job.XpText, label, FormatLike(job.XpText.text, Mathf.RoundToInt(job.XpModded)) + Suffix(job.XpMult), job.ShakeRoot, delegate
				{
					PlayBonusSound(job.GoldUi);
				});
			}
		}

		private static void ShowBonusPanel(EarnedGoldUI ui, float mult)
		{
			if ((Object)(object)ui == (Object)null)
			{
				return;
			}
			TMP_Text valueText;
			GameObject orCreateBonusPanel = GetOrCreateBonusPanel(ui, out valueText);
			if (!((Object)(object)orCreateBonusPanel == (Object)null))
			{
				orCreateBonusPanel.SetActive(true);
				if ((Object)(object)valueText != (Object)null)
				{
					valueText.text = FormatMult(mult);
				}
			}
		}

		private static void PlayBonusSound(EarnedGoldUI ui)
		{
			try
			{
				if ((Object)(object)ui != (Object)null)
				{
					OneShotSoundPlayer bonusGoldSoundPlayer = ui.bonusGoldSoundPlayer;
					if (bonusGoldSoundPlayer != null)
					{
						bonusGoldSoundPlayer.Play();
					}
				}
			}
			catch
			{
			}
		}

		private static IEnumerator WaitForValue(TMP_Text text, int value)
		{
			float timeout = Time.unscaledTime + 120f;
			float stableSince = -1f;
			while ((Object)(object)text != (Object)null && Time.unscaledTime < timeout)
			{
				if (((Behaviour)text).isActiveAndEnabled && ParseInt(text.text) == value)
				{
					if (stableSince < 0f)
					{
						stableSince = Time.unscaledTime;
					}
					else if (Time.unscaledTime - stableSince > 0.35f)
					{
						break;
					}
				}
				else
				{
					stableSince = -1f;
				}
				yield return null;
			}
		}

		private static IEnumerator WaitVisible(TMP_Text text)
		{
			float timeout = Time.unscaledTime + 120f;
			while ((Object)(object)text != (Object)null && !((Behaviour)text).isActiveAndEnabled && Time.unscaledTime < timeout)
			{
				yield return null;
			}
			float until = Time.unscaledTime + 0.3f;
			while (Time.unscaledTime < until)
			{
				yield return null;
			}
		}

		private static GameObject GetOrCreateBonusPanel(EarnedGoldUI ui, out TMP_Text valueText)
		{
			valueText = null;
			try
			{
				BonusGoldUI difficultyGoldBonus = ui.difficultyGoldBonus;
				GameObject val = ((difficultyGoldBonus != null) ? difficultyGoldBonus.Panel : null);
				if ((Object)(object)val == (Object)null)
				{
					return null;
				}
				Transform parent = val.transform.parent;
				Transform val2 = parent.Find("SBMod_GoldBonus");
				GameObject val3;
				if ((Object)(object)val2 != (Object)null)
				{
					val3 = ((Component)val2).gameObject;
				}
				else
				{
					val3 = Object.Instantiate<GameObject>(val, parent);
					((Object)val3).name = "SBMod_GoldBonus";
					Transform transform = val.transform;
					GameObject[] array = new GameObject[2];
					BonusGoldUI cursesGoldBonus = ui.cursesGoldBonus;
					array[0] = ((cursesGoldBonus != null) ? cursesGoldBonus.Panel : null);
					BonusGoldUI unusedCharacterGoldBonus = ui.unusedCharacterGoldBonus;
					array[1] = ((unusedCharacterGoldBonus != null) ? unusedCharacterGoldBonus.Panel : null);
					GameObject[] array2 = (GameObject[])(object)array;
					foreach (GameObject val4 in array2)
					{
						if ((Object)(object)val4 != (Object)null && (Object)(object)val4.transform.parent == (Object)(object)parent && val4.transform.GetSiblingIndex() > transform.GetSiblingIndex())
						{
							transform = val4.transform;
						}
					}
					val3.transform.SetSiblingIndex(transform.GetSiblingIndex() + 1);
				}
				string text = (((Object)(object)difficultyGoldBonus.Text != (Object)null) ? RelativePath(val.transform, difficultyGoldBonus.Text.transform) : null);
				foreach (TMP_Text componentsInChild in val3.GetComponentsInChildren<TMP_Text>(true))
				{
					if (text != null && RelativePath(val3.transform, componentsInChild.transform) == text)
					{
						foreach (LocalizeStringEvent component in ((Component)componentsInChild).GetComponents<LocalizeStringEvent>())
						{
							Object.DestroyImmediate((Object)(object)component);
						}
						valueText = componentsInChild;
					}
					else if ((Object)(object)val2 == (Object)null)
					{
						SettingsTab.BindText(componentsInChild, "bonus");
					}
				}
				val3.SetActive(false);
				return val3;
			}
			catch (Exception ex)
			{
				GoldXpMod.Log.Warning("[summary] bonus panel: " + ex.Message);
				return null;
			}
		}

		private static string RelativePath(Transform root, Transform t)
		{
			if ((Object)(object)t == (Object)(object)root)
			{
				return "";
			}
			StringBuilder stringBuilder = new StringBuilder(((Object)t).name);
			while ((Object)(object)t.parent != (Object)null && (Object)(object)t.parent != (Object)(object)root)
			{
				t = t.parent;
				stringBuilder.Insert(0, ((Object)t).name + "/");
			}
			return stringBuilder.ToString();
		}

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

		private static string Suffix(float mult)
		{
			return " <color=#FFD54A><size=75%>" + FormatMult(mult) + "</size></color>";
		}

		private static int ParseInt(string s)
		{
			if (string.IsNullOrEmpty(s))
			{
				return -1;
			}
			long num = 0L;
			bool flag = false;
			foreach (char c in s)
			{
				switch (c)
				{
				case '0':
				case '1':
				case '2':
				case '3':
				case '4':
				case '5':
				case '6':
				case '7':
				case '8':
				case '9':
					num = num * 10 + (c - 48);
					flag = true;
					if (num <= int.MaxValue)
					{
						continue;
					}
					return -1;
				default:
					continue;
				case '<':
					break;
				}
				break;
			}
			if (!flag)
			{
				return -1;
			}
			return (int)num;
		}

		private static string FormatLike(string sample, int value)
		{
			char? c = null;
			if (!string.IsNullOrEmpty(sample))
			{
				for (int i = 1; i < sample.Length - 1; i++)
				{
					if (!char.IsDigit(sample[i]) && char.IsDigit(sample[i - 1]) && char.IsDigit(sample[i + 1]))
					{
						c = sample[i];
						break;
					}
				}
			}
			string text = Math.Abs(value).ToString();
			if (!c.HasValue || text.Length <= 3)
			{
				return ((value < 0) ? "-" : "") + text;
			}
			StringBuilder stringBuilder = new StringBuilder();
			for (int j = 0; j < text.Length; j++)
			{
				if (j > 0 && (text.Length - j) % 3 == 0)
				{
					stringBuilder.Append(c.Value);
				}
				stringBuilder.Append(text[j]);
			}
			return ((value < 0) ? "-" : "") + stringBuilder;
		}
	}
	internal static class SaveDoctor
	{
		private const int MaxSaneRank = 1000;

		public static void RepairCorruptedRanks()
		{
			//IL_0064: 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_00b9: 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)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0216: Unknown result type (might be due to invalid IL or missing references)
			Instance log = GoldXpMod.Log;
			SaveManager instance = SingletonPersistent<SaveManager>.Instance;
			LocalPlayer instance2 = SingletonPersistent<LocalPlayer>.Instance;
			CharacterRankProgression val = ((instance2 != null) ? instance2.GetCharacterRankManager() : null);
			Dictionary<CharacterId, CharacterRankProgress> val2 = ((val != null) ? val.RankProgressPerCharacter : null);
			if ((Object)(object)instance == (Object)null || val2 == null)
			{
				return;
			}
			List<CharacterId> list = new List<CharacterId>();
			Enumerator<CharacterId, CharacterRankProgress> enumerator = val2.GetEnumerator();
			while (enumerator.MoveNext())
			{
				KeyValuePair<CharacterId, CharacterRankProgress> current = enumerator.Current;
				if (!IsSane(current.Value))
				{
					list.Add(current.Key);
				}
			}
			if (list.Count == 0)
			{
				return;
			}
			List<(int, PlayerSaveData)> list2 = LoadAllSlots(instance);
			foreach (CharacterId item3 in list)
			{
				string value = $"rank {val2[item3].CurrentRank} / progress {val2[item3].ProgressTowardsNextRank}";
				int num = 1;
				float num2 = 0f;
				string value2 = "reset to rank 1 (no healthy save slot)";
				foreach (var item4 in list2)
				{
					int item = item4.Item1;
					PlayerSaveData item2 = item4.Item2;
					Dictionary<CharacterId, CharacterRankProgress> rankProgressPerCharacter = item2.RankProgressPerCharacter;
					if (rankProgressPerCharacter != null && rankProgressPerCharacter.ContainsKey(item3) && IsSane(rankProgressPerCharacter[item3]))
					{
						num = rankProgressPerCharacter[item3].CurrentRank;
						num2 = rankProgressPerCharacter[item3].ProgressTowardsNextRank;
						value2 = $"restored from save slot {item} ({item2.SaveTimestamp})";
						break;
					}
				}
				RankXp.Suspended = true;
				try
				{
					val.SetRankProgression(item3, num, num2);
				}
				finally
				{
					RankXp.Suspended = false;
				}
				log.Warning($"[save] repaired {item3}: {value} → rank {num} / progress {num2} — {value2}");
			}
			instance.SavePlayerData();
			log.Msg($"[save] repaired {list.Count} character rank(s) and saved");
		}

		private static bool IsSane(CharacterRankProgress p)
		{
			if (p != null && p.CurrentRank >= 0 && p.CurrentRank <= 1000 && p.Prestige >= 0 && p.Prestige <= 1000 && !float.IsNaN(p.ProgressTowardsNextRank) && !float.IsInfinity(p.ProgressTowardsNextRank) && p.ProgressTowardsNextRank >= 0f && p.ProgressTowardsNextRank <= 1f)
			{
				if (p.ProgressTowardsNextRank != 0f)
				{
					return p.ProgressTowardsNextRank >= 1E-06f;
				}
				return true;
			}
			return false;
		}

		private static List<(int slot, PlayerSaveData data)> LoadAllSlots(SaveManager saveManager)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			List<(int, PlayerSaveData)> list = new List<(int, PlayerSaveData)>();
			for (int i = 0; i < SaveManager.SlotCount; i++)
			{
				try
				{
					IExternalSaveManager val = saveManager.CreateManagerForSlot(new RollingSaveSlot(i));
					if (val != null && val.SaveExists())
					{
						PlayerSaveData val2 = val.Load<PlayerSaveData>(SaveManager.SaveKey);
						if (val2 != null)
						{
							list.Add((i, val2));
						}
					}
				}
				catch (Exception ex)
				{
					GoldXpMod.Log.Warning($"[save] can't read slot {i}: {ex.GetType().Name}: {ex.Message}");
				}
			}
			list.Sort(delegate((int, PlayerSaveData) a, (int, PlayerSaveData) b)
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_000b: Unknown result type (might be due to invalid IL or missing references)
				//IL_001c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0021: Unknown result type (might be due to invalid IL or missing references)
				DateTime saveTimestamp = b.Item2.SaveTimestamp;
				long ticks = ((DateTime)(ref saveTimestamp)).Ticks;
				saveTimestamp = a.Item2.SaveTimestamp;
				return ticks.CompareTo(((DateTime)(ref saveTimestamp)).Ticks);
			});
			return list;
		}
	}
	internal static class SettingsTab
	{
		private sealed class Row
		{
			public Selector Selector;

			public Func<List<string>> Options;

			public Func<string> Current;

			public Action<string> Apply;
		}

		private const string ButtonName = "SBMod_Button";

		private const string PanelName = "SBMod_Panel";

		internal static readonly float[] Presets = new float[16]
		{
			0.5f, 1f, 1.25f, 1.5f, 2f, 2.5f, 3f, 4f, 5f, 7.5f,
			10f, 15f, 20f, 25f, 50f, 100f
		};

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

		private static bool _followsLanguage;

		public static void InjectPrefix(SettingsPanel __instance)
		{
			try
			{
				Inject(__instance);
			}
			catch (Exception value)
			{
				GoldXpMod.Log.Error($"[settings tab] inject failed: {value}");
			}
		}

		private static void Inject(SettingsPanel settings)
		{
			//IL_040a: Unknown result type (might be due to invalid IL or missing references)
			//IL_040f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0417: Unknown result type (might be due to invalid IL or missing references)
			//IL_0421: Expected O, but got Unknown
			List<SubPanel> subPanels = settings.subPanels;
			if (subPanels == null || subPanels.Count == 0)
			{
				return;
			}
			for (int i = 0; i < subPanels.Count; i++)
			{
				if ((Object)(object)subPanels[i].Panel != (Object)null && ((Object)subPanels[i].Panel).name == "SBMod_Panel")
				{
					return;
				}
			}
			SubPanel val = null;
			SubPanel val2 = null;
			int num = subPanels.Count;
			for (int j = 0; j < subPanels.Count; j++)
			{
				SubPanel val3 = subPanels[j];
				if (!((Object)(object)val3.Button == (Object)null) && !((Object)(object)val3.Panel == (Object)null))
				{
					if (((Object)val3.Panel).name.StartsWith("General"))
					{
						val = val3;
					}
					if (((Object)val3.Button).name.StartsWith("Controls"))
					{
						val2 = val3;
					}
					if (((Object)val3.Button).name.StartsWith("Credits"))
					{
						num = j;
					}
				}
			}
			if (val == null)
			{
				val = subPanels[0];
			}
			if (val2 == null)
			{
				val2 = subPanels[0];
			}
			if (!_followsLanguage)
			{
				_followsLanguage = true;
				Strings.OnLanguageChanged(delegate
				{
					MelonCoroutines.Start(RefreshNextFrame(onlyVisible: false));
				});
			}
			GameObject val4 = Object.Instantiate<GameObject>(((Component)val2.Button).gameObject, ((Component)val2.Button).transform.parent);
			((Object)val4).name = "SBMod_Button";
			if (num < subPanels.Count)
			{
				val4.transform.SetSiblingIndex(((Component)subPanels[num].Button).transform.GetSiblingIndex());
			}
			BindText(val4.GetComponentInChildren<TMP_Text>(true), "tab");
			MuteSerializedClicks(val4.GetComponent<Button>());
			SelectableButton component = val4.GetComponent<SelectableButton>();
			GameObject obj = Object.Instantiate<GameObject>(((Component)val.Panel).gameObject, ((Component)val.Panel).transform.parent);
			((Object)obj).name = "SBMod_Panel";
			obj.SetActive(false);
			NavigateablePanel component2 = obj.GetComponent<NavigateablePanel>();
			VerticalLayoutGroup componentInChildren = obj.GetComponentInChildren<VerticalLayoutGroup>(true);
			Transform val5 = ((componentInChildren != null) ? ((Component)componentInChildren).transform : null) ?? throw new InvalidOperationException("row container not found");
			Transform val6 = null;
			for (int num2 = 0; num2 < val5.childCount; num2++)
			{
				if (!((Object)(object)val6 == (Object)null))
				{
					break;
				}
				Transform child = val5.GetChild(num2);
				if (((Object)child).name.StartsWith("DamageNumbers"))
				{
					val6 = child;
				}
			}
			for (int num3 = 0; num3 < val5.childCount; num3++)
			{
				if (!((Object)(object)val6 == (Object)null))
				{
					break;
				}
				Transform child2 = val5.GetChild(num3);
				if ((Object)(object)((Component)child2).GetComponentInChildren<Selector>(true) != (Object)null)
				{
					val6 = child2;
				}
			}
			if ((Object)(object)val6 == (Object)null)
			{
				throw new InvalidOperationException("selector row template not found");
			}
			val6.SetParent((Transform)null, false);
			for (int num4 = val5.childCount - 1; num4 >= 0; num4--)
			{
				Object.DestroyImmediate((Object)(object)((Component)val5.GetChild(num4)).gameObject);
			}
			StripGameLogic(((Component)val6).gameObject);
			List<Selectable> list = new List<Selectable>
			{
				AddToggleRow(val5, val6, "enabled", GoldXpMod._enabledEntry),
				AddMultiplierRow(val5, val6, "gold", GoldXpMod._goldEntry),
				AddMultiplierRow(val5, val6, "xp", GoldXpMod._xpEntry),
				AddMultiplierRow(val5, val6, "rank", GoldXpMod._rankXpEntry),
				AddToggleRow(val5, val6, "reveal", GoldXpMod._revealEntry)
			};
			for (int num5 = 0; num5 < list.Count; num5++)
			{
				SetExplicit(list[num5], (num5 > 0) ? list[num5 - 1] : null, (num5 < list.Count - 1) ? list[num5 + 1] : null, null, null);
			}
			AddHint(val5, val6);
			Object.DestroyImmediate((Object)(object)((Component)val6).gameObject);
			component2.firstSelected = ((Component)list[0]).gameObject;
			RegisterInputReceivers(settings, ((Component)val.Panel).GetComponentInChildren<Selector>(true), list);
			SubPanel val7 = new SubPanel
			{
				Button = component,
				Panel = (Panel)(object)component2
			};
			subPanels.Insert(num, val7);
			((ButtonInteractionHook)component).OnClicked += Action.op_Implicit((Action)delegate
			{
				MelonCoroutines.Start(RefreshNextFrame());
			});
		}

		public static void LinkTabButtonsPostfix(SettingsPanel __instance)
		{
			try
			{
				List<SubPanel> subPanels = __instance.subPanels;
				if (subPanels == null)
				{
					return;
				}
				List<Button> list = new List<Button>();
				Selectable val = null;
				for (int i = 0; i < subPanels.Count; i++)
				{
					Button val2 = (((Object)(object)subPanels[i].Button != (Object)null) ? ((Component)subPanels[i].Button).GetComponent<Button>() : null);
					if ((Object)(object)val2 != (Object)null)
					{
						list.Add(val2);
					}
					if ((Object)(object)subPanels[i].Panel != (Object)null && ((Object)subPanels[i].Panel).name == "SBMod_Panel")
					{
						val = (Selectable)(object)((Component)subPanels[i].Panel).GetComponentInChildren<Selector>(true);
					}
				}
				if (list.Count < 2)
				{
					return;
				}
				list.Sort((Button a, Button b) => ((Component)a).transform.GetSiblingIndex().CompareTo(((Component)b).transform.GetSiblingIndex()));
				Selectable selectOnUp = ((Selectable)list[0]).navigation.selectOnUp;
				Selectable selectOnDown = ((Selectable)list[list.Count - 1]).navigation.selectOnDown;
				Selectable val3 = null;
				foreach (Button item in list)
				{
					if (((Object)item).name != "SBMod_Button" && (Object)(object)((Selectable)item).navigation.selectOnRight != (Object)null)
					{
						val3 = ((Selectable)item).navigation.selectOnRight;
						break;
					}
				}
				for (int num = 0; num < list.Count; num++)
				{
					Navigation navigation = ((Selectable)list[num]).navigation;
					bool flag = ((Object)list[num]).name == "SBMod_Button";
					SetExplicit((Selectable)(object)list[num], (Selectable)((num > 0) ? ((object)list[num - 1]) : ((object)selectOnUp)), (Selectable)((num < list.Count - 1) ? ((object)list[num + 1]) : ((object)selectOnDown)), flag ? null : navigation.selectOnLeft, flag ? (((Object)(object)val3 != (Object)null) ? val : null) : navigation.selectOnRight);
				}
			}
			catch (Exception value)
			{
				GoldXpMod.Log.Error($"[settings tab] navigation: {value}");
			}
		}

		private static void SetExplicit(Selectable s, Selectable up, Selectable down, Selectable left, Selectable right)
		{
			if (!((Object)(object)s == (Object)null))
			{
				Navigation navigation = s.navigation;
				navigation.mode = (Mode)4;
				navigation.selectOnUp = up;
				navigation.selectOnDown = down;
				navigation.selectOnLeft = left;
				navigation.selectOnRight = right;
				s.navigation = navigation;
			}
		}

		private static void RegisterInputReceivers(SettingsPanel settings, Selector nativeSelector, List<Selectable> selectors)
		{
			//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
			List<UIPanel> list = new List<UIPanel>();
			foreach (UIPanel item in ((Component)settings).GetComponentsInParent<UIPanel>(true))
			{
				list.Add(item);
			}
			foreach (UIPanel componentsInChild in ((Component)settings).GetComponentsInChildren<UIPanel>(true))
			{
				list.Add(componentsInChild);
			}
			UIPanel val = null;
			if ((Object)(object)nativeSelector != (Object)null)
			{
				foreach (UIPanel item2 in list)
				{
					List<IUIInputReceiver> inputReceivers = item2.inputReceivers;
					if (inputReceivers == null)
					{
						continue;
					}
					for (int i = 0; i < inputReceivers.Count; i++)
					{
						if (!((Object)(object)val == (Object)null))
						{
							break;
						}
						if (inputReceivers[i] != null && ((Il2CppObjectBase)inputReceivers[i]).Pointer == ((Il2CppObjectBase)nativeSelector).Pointer)
						{
							val = item2;
						}
					}
					if ((Object)(object)val != (Object)null)
					{
						break;
					}
				}
			}
			if (val == null)
			{
				val = ((list.Count > 0) ? list[0] : null);
			}
			if ((Object)(object)val == (Object)null)
			{
				GoldXpMod.Log.Warning("[settings tab] no UIPanel — arrow keys won't change values");
				return;
			}
			UIPanel val2 = val;
			if (val2.inputReceivers == null)
			{
				List<IUIInputReceiver> val3 = (val2.inputReceivers = new List<IUIInputReceiver>());
			}
			UIInputRouter instance = SingletonPersistent<UIInputRouter>.Instance;
			bool flag = false;
			if (((instance != null) ? instance.currentReceivers : null) != null && (Object)(object)nativeSelector != (Object)null)
			{
				for (int j = 0; j < instance.currentReceivers.Count; j++)
				{
					if (instance.currentReceivers[j] != null && ((Il2CppObjectBase)instance.currentReceivers[j]).Pointer == ((Il2CppObjectBase)nativeSelector).Pointer)
					{
						flag = true;
					}
				}
			}
			foreach (Selectable selector in selectors)
			{
				IUIInputReceiver val5 = (IUIInputReceiver)(((object)((Il2CppObjectBase)selector).TryCast<IUIInputReceiver>()) ?? ((object)new IUIInputReceiver(((Il2CppObjectBase)selector).Pointer)));
				val.inputReceivers.Add(val5);
				if (flag)
				{
					instance.Register(val5);
				}
			}
		}

		private static Selectable AddToggleRow(Transform container, Transform template, string labelKey, MelonPreferences_Entry<bool> entry)
		{
			return AddRow(container, template, labelKey, new Row
			{
				Options = () => new List<string>
				{
					Strings.Get("on"),
					Strings.Get("off")
				},
				Current = () => Strings.Get(entry.Value ? "on" : "off"),
				Apply = delegate(string option)
				{
					Store(entry, option == Strings.Get("on"));
				}
			});
		}

		private static Selectable AddMultiplierRow(Transform container, Transform template, string labelKey, MelonPreferences_Entry<float> entry)
		{
			return AddRow(container, template, labelKey, new Row
			{
				Options = delegate
				{
					List<float> list = new List<float>(Presets);
					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))
					{
						Store(entry, result);
					}
				}
			});
		}

		private static Selectable AddRow(Transform container, Transform template, string labelKey, Row row)
		{
			GameObject obj = Object.Instantiate<GameObject>(((Component)template).gameObject, container);
			((Object)obj).name = "SBMod_" + labelKey;
			obj.SetActive(true);
			Selector componentInChildren = obj.GetComponentInChildren<Selector>(true);
			foreach (TMP_Text componentsInChild in obj.GetComponentsInChildren<TMP_Text>(true))
			{
				if (!componentsInChild.transform.IsChildOf(((Component)componentInChildren).transform))
				{
					BindText(componentsInChild, labelKey);
				}
			}
			componentInChildren.localizationMode = (LocalizationMode)2;
			if ((Object)(object)componentInChildren.valueLocalizeString != (Object)null)
			{
				((Behaviour)componentInChildren.valueLocalizeString).enabled = false;
			}
			row.Selector = componentInChildren;
			componentInChildren.OnOptionSelected += Action<string>.op_Implicit((Action<string>)delegate(string option)
			{
				try
				{
					row.Apply(option);
				}
				catch (Exception value)
				{
					GoldXpMod.Log.Error($"[settings tab] {value}");
				}
			});
			Rows.Add(row);
			Show(row);
			return (Selectable)(object)componentInChildren;
		}

		private static void AddHint(Transform container, Transform template)
		{
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			GameObject obj = Object.Instantiate<GameObject>(((Component)template).gameObject, container);
			((Object)obj).name = "SBMod_Hint";
			obj.SetActive(true);
			Object.DestroyImmediate((Object)(object)((Component)obj.GetComponentInChildren<Selector>(true)).gameObject);
			TMP_Text componentInChildren = obj.GetComponentInChildren<TMP_Text>(true);
			BindText(componentInChildren, "hint");
			componentInChildren.fontSize *= 0.7f;
			componentInChildren.alignment = (TextAlignmentOptions)257;
			RectTransform rectTransform = componentInChildren.rectTransform;
			rectTransform.sizeDelta = new Vector2(0f, 140f);
			rectTransform.anchoredPosition = new Vector2(0f, -40f);
		}

		private static IEnumerator RefreshNextFrame(bool onlyVisible = true)
		{
			yield return null;
			Rows.RemoveAll((Row r) => (Object)(object)r.Selector == (Object)null);
			foreach (Row row in Rows)
			{
				if (!onlyVisible || ((Behaviour)row.Selector).isActiveAndEnabled)
				{
					Show(row);
				}
			}
		}

		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 void Store<T>(MelonPreferences_Entry<T> entry, T value)
		{
			if (!object.Equals(entry.Value, value))
			{
				entry.Value = value;
				GoldXpMod.SaveSettings();
			}
		}

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

		internal static void BindText(TMP_Text text, string key)
		{
			if ((Object)(object)text == (Object)null)
			{
				return;
			}
			foreach (LocalizeStringEvent component in ((Component)text).GetComponents<LocalizeStringEvent>())
			{
				Object.DestroyImmediate((Object)(object)component);
			}
			Strings.Bind(text, key);
		}

		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 Strings
	{
		public const string Tab = "tab";

		public const string Enabled = "enabled";

		public const string On = "on";

		public const string Off = "off";

		public const string Gold = "gold";

		public const string Xp = "xp";

		public const string RankXp = "rank";

		public const string Hint = "hint";

		public const string Bonus = "bonus";

		public const string Reveal = "reveal";

		public const string Matchmaking = "mm";

		private static readonly string[] Keys = new string[11]
		{
			"tab", "enabled", "on", "off", "gold", "xp", "rank", "hint", "bonus", "reveal",
			"mm"
		};

		private static readonly Dictionary<string, string[]> Table = new Dictionary<string, string[]>
		{
			["en"] = new string[11]
			{
				"Multipliers", "Mod Enabled", "On", "Off", "Gold per Run", "Run XP", "Wizard Rank XP", "Changes apply immediately. x1 = vanilla.\nIn co-op, run XP works when the host has the mod.", "Mod multiplier", "x2 reveal at the end of a run",
				"run XP multiplier is {0}: as host you change XP for the whole team. Set it to x1 to use matchmaking."
			},
			["ru"] = new string[11]
			{
				"Множители", "Мод включён", "Вкл", "Выкл", "Золото за забег", "Опыт в забеге", "Опыт мага (ранг)", "Изменения применяются сразу. x1 — как в оригинале.\nОпыт в забеге в коопе работает, если мод стоит у хоста.", "Множитель мода", "Анимация x2 в итогах забега",
				"множитель опыта в забеге {0}: у хоста он меняет опыт всей команде. Поставьте x1, чтобы включить подбор."
			},
			["uk"] = new string[11]
			{
				"Множники", "Мод увімкнено", "Увімк", "Вимк", "Золото за забіг", "Досвід у забігу", "Досвід мага (ранг)", "Зміни застосовуються одразу. x1 — як в оригіналі.\nДосвід у забігу в кооперативі працює, якщо мод встановлено в хоста.", "Множник мода", "Анімація x2 у підсумках забігу",
				"множник досвіду в забігу {0}: у хоста він змінює досвід усій команді. Поставте x1, щоб увімкнути підбір."
			},
			["de"] = new string[11]
			{
				"Multiplikatoren", "Mod aktiviert", "An", "Aus", "Gold pro Durchlauf", "EP im Durchlauf", "Magier-Rang-EP", "Änderungen gelten sofort. x1 = Original.\nIm Koop wirken die EP im Durchlauf, wenn der Host die Mod hat.", "Mod-Multiplikator", "x2-Animation am Ende des Laufs",
				"Lauf-EP-Multiplikator ist {0}: Als Host änderst du die EP des ganzen Teams. Stelle ihn auf x1, um Matchmaking zu nutzen."
			},
			["fr"] = new string[11]
			{
				"Multiplicateurs", "Mod activé", "Activé", "Désactivé", "Or par partie", "XP de partie", "XP de rang du mage", "Les changements s'appliquent immédiatement. x1 = jeu d'origine.\nEn coop, l'XP de partie fonctionne si l'hôte a le mod.", "Multiplicateur du mod", "Animation x2 en fin de partie",
				"le multiplicateur d'XP de partie est {0} : en tant qu'hôte, vous modifiez l'XP de toute l'équipe. Mettez-le à x1 pour utiliser le matchmaking."
			},
			["it"] = new string[11]
			{
				"Moltiplicatori", "Mod attiva", "Attivo", "Disattivo", "Oro per partita", "PE della partita", "PE rango mago", "Le modifiche si applicano subito. x1 = originale.\nIn co-op, i PE della partita funzionano se l'host ha la mod.", "Moltiplicatore mod", "Animazione x2 a fine partita",
				"il moltiplicatore PE della partita è {0}: come host cambi i PE di tutta la squadra. Impostalo a x1 per usare il matchmaking."
			},
			["nl"] = new string[11]
			{
				"Vermenigvuldigers", "Mod ingeschakeld", "Aan", "Uit", "Goud per run", "Run-XP", "Tovenaarsrang-XP", "Wijzigingen gelden direct. x1 = origineel.\nIn co-op werkt run-XP als de host de mod heeft.", "Mod-vermenigvuldiger", "x2-animatie aan het einde van de run",
				"run-XP-vermenigvuldiger is {0}: als host verander je de XP van het hele team. Zet hem op x1 om matchmaking te gebruiken."
			},
			["pl"] = new string[11]
			{
				"Mnożniki", "Mod włączony", "Wł.", "Wył.", "Złoto za wyprawę", "PD w wyprawie", "PD rangi maga", "Zmiany działają od razu. x1 = oryginał.\nW trybie co-op PD w wyprawie działa, jeśli host ma moda.", "Mnożnik moda", "Animacja x2 w podsumowaniu wyprawy",
				"mnożnik PD w wyprawie to {0}: jako host zmieniasz PD całej drużynie. Ustaw x1, aby używać dobierania."
			},
			["pt-br"] = new string[11]
			{
				"Multiplicadores", "Mod ativado", "Ligado", "Desligado", "Ouro por partida", "XP da partida", "XP de nível do mago", "As mudanças valem na hora. x1 = original.\nNo co-op, o XP da partida funciona se o anfitrião tiver o mod.", "Multiplicador do mod", "Animação x2 no fim da partida",
				"o multiplicador de XP da partida é {0}: como anfitrião, você muda o XP de toda a equipe. Coloque x1 para usar o matchmaking."
			},
			["pt"] = new string[11]
			{
				"Multiplicadores", "Mod ativado", "Ligado", "Desligado", "Ouro por partida", "XP da partida", "XP de nível do mago", "As alterações aplicam-se de imediato.