Decompiled source of RunCustomizer v1.1.2
Mods/RunCustomizer.dll
Decompiled 3 weeks ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using HarmonyLib; using Il2Cpp; using Il2CppInterop.Common; using Il2CppInterop.Runtime; using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppSystem; using Il2CppSystem.Collections.Generic; using Il2CppSystem.Reflection; using Il2CppTMPro; using MelonLoader; using MelonLoader.Preferences; using Microsoft.CodeAnalysis; using RunCustomizer; using SpellBrigade.ModMenu; using SpellBrigade.Shared; using Unity.Netcode; using UnityEngine; using UnityEngine.Events; using UnityEngine.InputSystem; using UnityEngine.Localization; using UnityEngine.Localization.Components; using UnityEngine.Localization.Settings; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: MelonInfo(typeof(RunCustomizerMod), "Run Customizer", "1.1.2", "Relsev", null)] [assembly: MelonGame("BoltBlasterGames", "TheSpellBrigade")] [assembly: MelonOptionalDependencies(new string[] { "ModMenu" })] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("RunCustomizer")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.1.2.0")] [assembly: AssemblyInformationalVersion("1.1.2+1856b18f4bf94ea33a9b586b39e9ee6f647e5453")] [assembly: AssemblyProduct("RunCustomizer")] [assembly: AssemblyTitle("RunCustomizer")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.1.2.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace SpellBrigade.Shared { internal static class Hooks { public static bool Patch(Harmony harmony, Instance log, Type target, string method, Type patchClass, string prefix = null, string postfix = null) { return Patch(harmony, log, AccessTools.Method(target, method, (Type[])null, (Type[])null), patchClass, prefix, postfix, target.Name + "." + method); } public static bool Patch(Harmony harmony, Instance log, MethodBase original, Type patchClass, string prefix = null, string postfix = null, string name = null) { //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) if (name == null) { name = ((original != null) ? (original.DeclaringType?.Name + "." + original.Name) : ((prefix ?? postfix) + " target")); } try { if (original == null) { throw new MissingMethodException(name + " not found"); } List<string> list = SharedCodeGuard.FindMethodsSharingCode(original); if (list.Count > 0) { log.Warning($"skipped {name}: its native code is shared with {list.Count} other method(s), patching would break them (e.g. {string.Join(", ", list.GetRange(0, Math.Min(5, list.Count)))})"); return false; } harmony.Patch(original, (prefix == null) ? ((HarmonyMethod)null) : new HarmonyMethod(AccessTools.Method(patchClass, prefix, (Type[])null, (Type[])null)), (postfix == null) ? ((HarmonyMethod)null) : new HarmonyMethod(AccessTools.Method(patchClass, postfix, (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); return true; } catch (Exception ex) { log.Error($"failed to hook {name} — this part is disabled. {ex.GetType().Name}: {ex.Message}"); return false; } } } internal sealed class Localizer { private readonly string[] _keys; private readonly Dictionary<string, string[]> _table; private readonly Func<Instance> _log; private readonly Dictionary<string, bool> _fontSupport = new Dictionary<string, bool>(); private readonly List<(TMP_Text text, Func<string> value)> _bound = new List<(TMP_Text, Func<string>)>(); private readonly List<Action> _listeners = new List<Action>(); private string[] _current; private string _code; private TMP_Text _fontSample; private bool _subscribed; public Localizer(string[] keys, Dictionary<string, string[]> table, Func<Instance> log) { _keys = keys; _table = table; _log = log; } public string Get(string key) { if (_current == null || _code != LocaleCode()) { _current = Resolve(_fontSample); } int num = Array.IndexOf(_keys, key); if (num < 0 || num >= _current.Length) { return key; } return _current[num]; } public void Bind(TMP_Text text, Func<string> value, TMP_Text fontSample = null) { if (!((Object)(object)text == (Object)null)) { EnsureSubscribed(); if (_current == null) { _current = Resolve(fontSample ?? text); } text.text = value(); _bound.Add((text, value)); } } public void Bind(TMP_Text text, string key, TMP_Text fontSample = null) { Bind(text, () => Get(key), fontSample); } public void OnLanguageChanged(Action listener) { EnsureSubscribed(); _listeners.Add(listener); } public void UseFont(TMP_Text sample) { _fontSample = sample; _current = null; EnsureSubscribed(); } private void EnsureSubscribed() { if (_subscribed) { return; } _subscribed = true; try { LocalizationSettings.SelectedLocaleChanged += Action<Locale>.op_Implicit((Action<Locale>)delegate { Refresh(); }); } catch (Exception ex) { Instance obj = _log(); if (obj != null) { obj.Warning("[lang] can't follow language changes: " + ex.Message); } } } private void Refresh() { _bound.RemoveAll(((TMP_Text text, Func<string> value) b) => (Object)(object)b.text == (Object)null); _current = Resolve((_bound.Count > 0) ? _bound[0].text : _fontSample); foreach (var (val, func) in _bound) { val.text = func(); } foreach (Action listener in _listeners) { try { listener(); } catch (Exception ex) { Instance obj = _log(); if (obj != null) { obj.Warning("[lang] " + ex.Message); } } } } private static string LocaleCode() { try { Locale selectedLocale = LocalizationSettings.SelectedLocale; return ((selectedLocale != null) ? selectedLocale.Identifier.Code : null) ?? "en"; } catch { return "en"; } } private string[] Resolve(TMP_Text fontSample) { string text = (_code = LocaleCode()); string text2 = TableKey(text); if (text2 != "en" && (Object)(object)fontSample != (Object)null && !FontHasAll(fontSample, text2)) { Instance obj = _log(); if (obj != null) { obj.Warning("[lang] font lacks glyphs for '" + text + "', using English"); } text2 = "en"; } return _table[text2]; } private string TableKey(string code) { code = code.ToLowerInvariant().Replace('_', '-'); string text = (code.StartsWith("zh") ? ((code.Contains("hant") || code.Contains("tw") || code.Contains("hk")) ? "zh-hant" : "zh-hans") : (code.StartsWith("pt") ? (code.Contains("br") ? "pt-br" : "pt") : ((!code.StartsWith("es")) ? code.Split('-')[0] : ((code == "es" || code.Contains("es-es")) ? "es" : "es-mx")))); if (_table.ContainsKey(text)) { return text; } string text2 = text.Split('-')[0]; if (!_table.ContainsKey(text2)) { return "en"; } return text2; } private bool FontHasAll(TMP_Text sample, string key) { if (_fontSupport.TryGetValue(key, out var value)) { return value; } value = true; try { TMP_FontAsset font = sample.font; if ((Object)(object)font != (Object)null) { string[] array = _table[key]; foreach (string text in array) { foreach (char c in text) { if (c > '\u007f' && !char.IsWhiteSpace(c) && !font.HasCharacter(c, true, true)) { value = false; break; } } if (!value) { break; } } } } catch { value = true; } _fontSupport[key] = value; return value; } } internal static class MatchmakingGuard { private const string BlockersKey = "SpellBrigade.Matchmaking.Blockers"; private const string OwnerKey = "SpellBrigade.Matchmaking.Owner"; private const string PanelName = "SpellBrigade_MatchmakingLock"; private static readonly Color Gold = new Color(0.96f, 0.78f, 0.35f); private static bool _owner; private static bool _wasBlocked; private static Instance _log; private static MatchmakingPreferencesModal _modal; private static TMP_Text _title; private static TMP_Text _body; private static float _pulse; private static float _nextCheck; private const string Title = "title"; private const string Intro = "intro"; private const string Footer = "footer"; private static readonly Localizer Text = new Localizer(new string[3] { "title", "intro", "footer" }, new Dictionary<string, string[]> { ["en"] = new string[3] { "Matchmaking is locked by mods", "These mods change the game for everyone in the lobby, so players without them can't be matched with you:", "Play with friends: invite them or share the lobby code. Matchmaking comes back as soon as nothing above applies." }, ["ru"] = new string[3] { "Подбор игроков заблокирован модами", "Эти моды меняют игру для всех в лобби, поэтому игроков без них к вам не подберёт:", "Играйте с друзьями: пригласите их или дайте код лобби. Подбор вернётся сам, как только ничего из списка не будет мешать." }, ["uk"] = new string[3] { "Підбір гравців заблоковано модами", "Ці моди змінюють гру для всіх у лобі, тому гравців без них до вас не підбере:", "Грайте з друзями: запросіть їх або дайте код лобі. Підбір повернеться сам, щойно нічого зі списку не заважатиме." }, ["de"] = new string[3] { "Matchmaking ist durch Mods gesperrt", "Diese Mods verändern das Spiel für alle in der Lobby, daher kannst du nicht mit Spielern ohne sie zusammengebracht werden:", "Spiele mit Freunden: Lade sie ein oder teile den Lobby-Code. Matchmaking kommt zurück, sobald nichts davon mehr zutrifft." }, ["fr"] = new string[3] { "Matchmaking bloqué par des mods", "Ces mods modifient la partie pour tout le salon, vous ne pouvez donc pas être associé à des joueurs qui ne les ont pas :", "Jouez avec des amis : invitez-les ou partagez le code du salon. Le matchmaking revient dès que plus rien ci-dessus ne s'applique." }, ["it"] = new string[3] { "Matchmaking bloccato dalle mod", "Queste mod cambiano la partita per tutta la lobby, quindi non puoi essere abbinato a chi non le ha:", "Gioca con gli amici: invitali o condividi il codice della lobby. Il matchmaking torna appena nulla di tutto ciò si applica." }, ["nl"] = new string[3] { "Matchmaking geblokkeerd door mods", "Deze mods veranderen het spel voor iedereen in de lobby, dus je wordt niet gekoppeld aan spelers zonder deze mods:", "Speel met vrienden: nodig ze uit of deel de lobbycode. Matchmaking komt terug zodra niets hiervan meer geldt." }, ["pl"] = new string[3] { "Dobieranie graczy zablokowane przez mody", "Te mody zmieniają grę wszystkim w lobby, więc gracze bez nich nie zostaną do ciebie dobrani:", "Graj ze znajomymi: zaproś ich lub podaj kod lobby. Dobieranie wróci samo, gdy nic z listy nie będzie przeszkadzać." }, ["pt-br"] = new string[3] { "Matchmaking bloqueado por mods", "Estes mods mudam o jogo para todos no lobby, então jogadores sem eles não serão pareados com você:", "Jogue com amigos: convide-os ou compartilhe o código do lobby. O matchmaking volta assim que nada acima se aplicar." }, ["pt"] = new string[3] { "Matchmaking bloqueado por mods", "Estes mods mudam o jogo para todos no lobby, por isso jogadores sem eles não serão emparelhados contigo:", "Joga com amigos: convida-os ou partilha o código do lobby. O matchmaking volta assim que nada acima se aplicar." }, ["es"] = new string[3] { "Emparejamiento bloqueado por mods", "Estos mods cambian la partida para toda la sala, así que no se te emparejará con jugadores que no los tengan:", "Juega con amigos: invítalos o comparte el código de sala. El emparejamiento vuelve en cuanto nada de lo anterior aplique." }, ["es-mx"] = new string[3] { "Emparejamiento bloqueado por mods", "Estos mods cambian la partida para toda la sala, así que no se te emparejará con jugadores que no los tengan:", "Juega con amigos: invítalos o comparte el código de sala. El emparejamiento regresa en cuanto nada de lo anterior aplique." }, ["ja"] = new string[3] { "MODによりマッチングがロックされています", "これらのMODはロビー全員のゲームを変えるため、MODのないプレイヤーとはマッチングしません:", "フレンドと遊びましょう:招待するかロビーコードを共有してください。上記が当てはまらなくなればマッチングは戻ります。" }, ["ko"] = new string[3] { "모드로 인해 매칭이 잠겼습니다", "이 모드들은 로비 전체의 게임을 바꾸므로, 모드가 없는 플레이어와는 매칭되지 않습니다:", "친구와 플레이하세요: 초대하거나 로비 코드를 공유하세요. 위 항목이 모두 해당되지 않으면 매칭이 다시 열립니다." }, ["zh-hans"] = new string[3] { "匹配已被模组锁定", "这些模组会改变大厅里所有人的游戏,因此不会为你匹配没有这些模组的玩家:", "和好友一起玩:邀请他们或分享大厅代码。只要上面各项都不再适用,匹配就会恢复。" }, ["zh-hant"] = new string[3] { "配對已被模組鎖定", "這些模組會改變大廳裡所有人的遊戲,因此不會為你配對沒有這些模組的玩家:", "和好友一起玩:邀請他們或分享大廳代碼。只要上面各項都不再適用,配對就會恢復。" }, ["th"] = new string[3] { "การจ\u0e31บค\u0e39\u0e48ถ\u0e39กล\u0e47อกโดยม\u0e47อด", "ม\u0e47อดเหล\u0e48าน\u0e35\u0e49เปล\u0e35\u0e48ยนเกมของท\u0e38กคนในล\u0e47อบบ\u0e35\u0e49 จ\u0e36งจะไม\u0e48จ\u0e31บค\u0e39\u0e48ค\u0e38ณก\u0e31บผ\u0e39\u0e49เล\u0e48นท\u0e35\u0e48ไม\u0e48ม\u0e35ม\u0e47อด:", "เล\u0e48นก\u0e31บเพ\u0e37\u0e48อน: เช\u0e34ญเพ\u0e37\u0e48อนหร\u0e37อแชร\u0e4cรห\u0e31สล\u0e47อบบ\u0e35\u0e49 การจ\u0e31บค\u0e39\u0e48จะกล\u0e31บมาเม\u0e37\u0e48อไม\u0e48ม\u0e35ข\u0e49อใดด\u0e49านบนเข\u0e49าเง\u0e37\u0e48อนไข" } }, () => _log); private static bool _prepareRestore; public static bool Blocked => Reasons().Count > 0; private static MatchmakingPreferencesManager Prefs => Singleton<MatchmakingPreferencesManager>.Instance; public static void Register(Harmony harmony, Instance log, string modName, Func<string> reason) { AppDomain currentDomain = AppDomain.CurrentDomain; List<Func<string>> list = currentDomain.GetData("SpellBrigade.Matchmaking.Blockers") as List<Func<string>>; if (list == null) { currentDomain.SetData("SpellBrigade.Matchmaking.Blockers", list = new List<Func<string>>()); } list.Add(delegate { string text = reason(); return (text == null) ? null : ("<b>" + modName + "</b> — " + text); }); object data = currentDomain.GetData("SpellBrigade.Matchmaking.Owner"); if (!(data is bool) || !(bool)data) { currentDomain.SetData("SpellBrigade.Matchmaking.Owner", true); _owner = true; _log = log; int num = 0; num += Hook(harmony, typeof(RunPreparationManager), "Prepare", "PreparePrefix", "PreparePostfix"); num += Hook(harmony, typeof(RunPreparationManager), "CanUseMatchmaking", "CanUsePrefix"); num += Hook(harmony, typeof(MatchmakingToggle), "Toggle", "TogglePrefix"); num += Hook(harmony, typeof(MatchmakingPreferencesModal), "OnIsMatchmakingEnabledChanged", "EnabledChangedPrefix"); num += Hook(harmony, typeof(MatchmakingPreferencesModal), "RefreshPanel", null, "RefreshPanelPostfix"); log.Msg($"matchmaking guard: {num}/5 hooks"); } } private static int Hook(Harmony harmony, Type type, string method, string prefix, string postfix = null) { return Hooks.Patch(harmony, _log, type, method, typeof(MatchmakingGuard), prefix, postfix) ? 1 : 0; } public static List<string> Reasons() { List<string> list = new List<string>(); if (!(AppDomain.CurrentDomain.GetData("SpellBrigade.Matchmaking.Blockers") is List<Func<string>> list2)) { return list; } foreach (Func<string> item in list2) { try { string text = item(); if (text != null) { list.Add(text); } } catch { } } return list; } private static void PreparePrefix() { List<string> list = Reasons(); MatchmakingPreferencesManager prefs = Prefs; if (list.Count != 0 && !((Object)(object)prefs == (Object)null) && prefs.IsMatchmakingEnabled) { prefs.IsMatchmakingEnabled = false; _prepareRestore = true; Instance log = _log; if (log != null) { log.Msg("run starts without matchmaking — " + StripTags(string.Join("; ", list))); } } } private static void PreparePostfix() { if (_prepareRestore) { _prepareRestore = false; MatchmakingPreferencesManager prefs = Prefs; if ((Object)(object)prefs != (Object)null) { prefs.IsMatchmakingEnabled = true; } } } private static bool CanUsePrefix(ref bool __result) { if (!Blocked) { return true; } __result = false; return false; } private static bool TogglePrefix(MatchmakingToggle __instance) { if (!Blocked || !IsMatchmakingToggle(__instance)) { return true; } _pulse = 1f; return false; } private static bool EnabledChangedPrefix(MatchmakingPreferencesModal __instance, bool isOn) { if (!isOn || !Blocked) { return true; } RefreshPanelPostfix(__instance); return false; } private static bool IsMatchmakingToggle(MatchmakingToggle toggle) { if ((Object)(object)_modal != (Object)null && (Object)(object)_modal.isMatchmakingEnabledToggle != (Object)null) { return ((Il2CppObjectBase)_modal.isMatchmakingEnabledToggle).Pointer == ((Il2CppObjectBase)toggle).Pointer; } return false; } private static void RefreshPanelPostfix(MatchmakingPreferencesModal __instance) { try { _modal = __instance; Apply(__instance, Reasons()); } catch (Exception ex) { Instance log = _log; if (log != null) { log.Warning("matchmaking window: " + ex.Message); } } } private static void Apply(MatchmakingPreferencesModal modal, List<string> reasons) { bool flag = reasons.Count > 0; MatchmakingToggle isMatchmakingEnabledToggle = modal.isMatchmakingEnabledToggle; if ((Object)(object)isMatchmakingEnabledToggle != (Object)null) { if (flag) { isMatchmakingEnabledToggle.isOn = false; isMatchmakingEnabledToggle.RefreshUI(); } (((Component)isMatchmakingEnabledToggle).GetComponent<CanvasGroup>() ?? ((Component)isMatchmakingEnabledToggle).gameObject.AddComponent<CanvasGroup>()).alpha = (flag ? 0.4f : 1f); } if (flag && (Object)(object)modal.matchmakingPreferencesObject != (Object)null) { modal.matchmakingPreferencesObject.SetActive(false); } Transform val = ((Component)modal).transform.Find("SpellBrigade_MatchmakingLock"); if ((Object)(object)val == (Object)null && flag) { val = BuildPanel(modal); } if ((Object)(object)val == (Object)null) { return; } ((Component)val).gameObject.SetActive(flag); if (!flag) { return; } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append(Text.Get("intro")).Append('\n'); foreach (string reason in reasons) { stringBuilder.Append("\n• ").Append(reason); } stringBuilder.Append("\n\n<alpha=#B3>").Append(Text.Get("footer")); _title.text = Text.Get("title"); _body.text = stringBuilder.ToString(); } private static Transform BuildPanel(MatchmakingPreferencesModal modal) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_0191: Unknown result type (might be due to invalid IL or missing references) //IL_01a8: Unknown result type (might be due to invalid IL or missing references) Transform obj = ((Component)modal).transform.Find("Matchmaking_Description"); TMP_Text val = ((obj != null) ? ((Component)obj).GetComponent<TMP_Text>() : null) ?? ((Component)modal).GetComponentInChildren<TMP_Text>(true); if ((Object)(object)val == (Object)null) { return null; } RectTransform val2 = new GameObject("SpellBrigade_MatchmakingLock").AddComponent<RectTransform>(); ((Transform)val2).SetParent(((Component)modal).transform, false); Vector2 val3 = default(Vector2); ((Vector2)(ref val3))..ctor(0f, 1f); val2.pivot = val3; Vector2 anchorMin = (val2.anchorMax = val3); val2.anchorMin = anchorMin; val2.anchoredPosition = new Vector2(250f, -1060f); val2.sizeDelta = new Vector2(2250f, 760f); float num = (val.enableAutoSizing ? val.fontSizeMax : val.fontSize); _title = CloneText(val, (Transform)(object)val2, "Title", num * 1.25f, Gold, (FontStyles)1); RectTransform rectTransform = _title.rectTransform; rectTransform.anchorMin = new Vector2(0f, 1f); rectTransform.anchorMax = new Vector2(1f, 1f); rectTransform.pivot = new Vector2(0f, 1f); rectTransform.sizeDelta = new Vector2(0f, num * 1.8f); rectTransform.anchoredPosition = Vector2.zero; _body = CloneText(val, (Transform)(object)val2, "Body", num, Color.white, (FontStyles)0); RectTransform rectTransform2 = _body.rectTransform; rectTransform2.anchorMin = Vector2.zero; rectTransform2.anchorMax = Vector2.one; rectTransform2.pivot = new Vector2(0f, 1f); rectTransform2.offsetMin = Vector2.zero; rectTransform2.offsetMax = new Vector2(0f, (0f - num) * 2f); _body.textWrappingMode = (TextWrappingModes)1; _body.enableAutoSizing = true; _body.fontSizeMin = num * 0.6f; _body.fontSizeMax = num; return (Transform)(object)val2; } private static TMP_Text CloneText(TMP_Text template, Transform parent, string name, float size, Color color, FontStyles style) { //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) TMP_Text component = Object.Instantiate<GameObject>(((Component)template).gameObject, parent).GetComponent<TMP_Text>(); foreach (LocalizeStringEvent component2 in ((Component)component).GetComponents<LocalizeStringEvent>()) { Object.Destroy((Object)(object)component2); } foreach (MonoBehaviour component3 in ((Component)component).GetComponents<MonoBehaviour>()) { if ((Object)(object)component3 != (Object)null && (Object)(object)((Il2CppObjectBase)component3).TryCast<TMP_Text>() == (Object)null) { Object.Destroy((Object)(object)component3); } } ((Object)component).name = name; component.enableAutoSizing = false; component.fontSize = size; ((Graphic)component).color = color; component.fontStyle = style; component.alignment = (TextAlignmentOptions)257; component.textWrappingMode = (TextWrappingModes)0; component.overflowMode = (TextOverflowModes)0; ((Graphic)component).raycastTarget = false; return component; } private static string StripTags(string s) { return Regex.Replace(s, "<.*?>", ""); } public static void Tick() { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) if (!_owner) { return; } if (_pulse > 0f && (Object)(object)_title != (Object)null) { _pulse = Mathf.Max(0f, _pulse - Time.unscaledDeltaTime * 2.5f); _title.transform.localScale = Vector3.one * (1f + 0.08f * _pulse); ((Graphic)_title).color = Color.Lerp(Gold, new Color(1f, 0.45f, 0.35f), _pulse); } if (Time.unscaledTime < _nextCheck) { return; } _nextCheck = Time.unscaledTime + 0.25f; List<string> list = Reasons(); bool flag = list.Count > 0; if (flag != _wasBlocked) { _wasBlocked = flag; Instance log = _log; if (log != null) { log.Msg(flag ? ("matchmaking locked — " + StripTags(string.Join("; ", list))) : "matchmaking unlocked"); } if ((Object)(object)_modal != (Object)null && ((Component)_modal).gameObject.activeInHierarchy) { try { _modal.RefreshPanel(); } catch { } } } else if (flag && (Object)(object)_modal != (Object)null && ((Component)_modal).gameObject.activeInHierarchy && (Object)(object)_body != (Object)null) { Apply(_modal, list); } } } internal static class SharedCodeGuard { private static Dictionary<IntPtr, List<IntPtr>> _methodsByCode; public static List<string> FindMethodsSharingCode(MethodBase generatedMethod) { List<string> list = new List<string>(); if (!(Il2CppInteropUtils.GetIl2CppMethodInfoPointerFieldForGeneratedMethod(generatedMethod)?.GetValue(null) is IntPtr intPtr) || intPtr == IntPtr.Zero) { return list; } IntPtr intPtr2 = Marshal.ReadIntPtr(intPtr); if (intPtr2 == IntPtr.Zero) { return list; } if (_methodsByCode == null) { _methodsByCode = BuildIndex(); } if (!_methodsByCode.TryGetValue(intPtr2, out var value)) { return list; } foreach (IntPtr item in value) { if (item != intPtr) { list.Add(Describe(item)); } } return list; } private static string Describe(IntPtr method) { IntPtr intPtr = IL2CPP.il2cpp_method_get_class(method); string text = IL2CPP.il2cpp_class_get_namespace_(intPtr); return (string.IsNullOrEmpty(text) ? IL2CPP.il2cpp_class_get_name_(intPtr) : (text + "." + IL2CPP.il2cpp_class_get_name_(intPtr))) + "::" + IL2CPP.il2cpp_method_get_name_(method); } private unsafe static Dictionary<IntPtr, List<IntPtr>> BuildIndex() { Dictionary<IntPtr, List<IntPtr>> dictionary = new Dictionary<IntPtr, List<IntPtr>>(); uint num = 0u; IntPtr* ptr = IL2CPP.il2cpp_domain_get_assemblies(IL2CPP.il2cpp_domain_get(), ref num); for (uint num2 = 0u; num2 < num; num2++) { IntPtr intPtr = IL2CPP.il2cpp_assembly_get_image(ptr[num2]); uint num3 = IL2CPP.il2cpp_image_get_class_count(intPtr); for (uint num4 = 0u; num4 < num3; num4++) { IntPtr intPtr2 = IL2CPP.il2cpp_image_get_class(intPtr, num4); if (intPtr2 == IntPtr.Zero) { continue; } IntPtr zero = IntPtr.Zero; IntPtr intPtr3; while ((intPtr3 = IL2CPP.il2cpp_class_get_methods(intPtr2, ref zero)) != IntPtr.Zero) { IntPtr intPtr4 = Marshal.ReadIntPtr(intPtr3); if (!(intPtr4 == IntPtr.Zero)) { if (!dictionary.TryGetValue(intPtr4, out var value)) { value = (dictionary[intPtr4] = new List<IntPtr>(1)); } value.Add(intPtr3); } } } } return dictionary; } } internal static class WheelSelect { private sealed class State { public ScrollRect Blocked; public float BlockedSensitivity; public Vector2 LastMouse = new Vector2(-1f, -1f); } private const string FrameKey = "SpellBrigade.WheelSelect.Frame"; private const string CustomKey = "SpellBrigade.WheelSelect.Custom"; private static readonly State Own = new State(); private static List<(GameObject target, Action<int> step)> Custom { get { AppDomain currentDomain = AppDomain.CurrentDomain; if (currentDomain.GetData("SpellBrigade.WheelSelect.Custom") is List<(GameObject, Action<int>)> result) { return result; } List<(GameObject, Action<int>)> list = new List<(GameObject, Action<int>)>(); currentDomain.SetData("SpellBrigade.WheelSelect.Custom", list); return list; } } public static void Register(GameObject target, Action<int> step) { if ((Object)(object)target != (Object)null && step != null) { Custom.Add((target, step)); } } public static void Tick() { try { TickCore(); } catch { } } private static void TickCore() { //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) int frameCount = Time.frameCount; AppDomain currentDomain = AppDomain.CurrentDomain; if (currentDomain.GetData("SpellBrigade.WheelSelect.Frame") is int num && num == frameCount) { return; } currentDomain.SetData("SpellBrigade.WheelSelect.Frame", frameCount); object data = currentDomain.GetData("SpellBrigade.Modal"); if (data is bool && (bool)data) { return; } Mouse current = Mouse.current; if (current == null) { return; } State own = Own; Vector2 val = ((InputControl<Vector2>)(object)((Pointer)current).position).ReadValue(); float y = ((InputControl<Vector2>)(object)current.scroll).ReadValue().y; Vector2 val2 = val - own.LastMouse; if (!(((Vector2)(ref val2)).sqrMagnitude > 0.25f) && Mathf.Abs(y) < 0.01f) { return; } own.LastMouse = val; var (val3, action, val4) = Hovered(val); BlockScroll(own, ((Object)(object)val4 != (Object)null) ? ScrollRectAbove(val4) : null); if (Mathf.Abs(y) < 0.01f || (Object)(object)val4 == (Object)null) { return; } int num2 = ((y > 0f) ? 1 : (-1)); try { if ((Object)(object)val3 != (Object)null) { if (num2 > 0) { val3.SelectNext(); } else { val3.SelectPrevious(); } } else { action?.Invoke(num2); } } catch { } } private static (Selector selector, Action<int> custom, RectTransform rect) Hovered(Vector2 position) { //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) List<(GameObject target, Action<int> step)> custom = Custom; custom.RemoveAll(((GameObject target, Action<int> step) c) => (Object)(object)c.target == (Object)null); foreach (var (val, item) in custom) { if (val.activeInHierarchy && Contains(val.GetComponent<RectTransform>(), position)) { return (selector: null, custom: item, rect: val.GetComponent<RectTransform>()); } } foreach (Selector item2 in Object.FindObjectsOfType<Selector>()) { if (!((Object)(object)item2 == (Object)null) && ((Selectable)item2).IsInteractable()) { RectTransform component = ((Component)item2).GetComponent<RectTransform>(); if (Contains(component, position)) { return (selector: item2, custom: null, rect: component); } } } return (selector: null, custom: null, rect: null); } private static bool Contains(RectTransform rect, Vector2 position) { //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)rect == (Object)null) { return false; } Canvas val = null; Transform val2 = ((Component)rect).transform; while ((Object)(object)val2 != (Object)null) { CanvasGroup component = ((Component)val2).GetComponent<CanvasGroup>(); if ((Object)(object)component != (Object)null && (component.alpha < 0.01f || !component.blocksRaycasts)) { return false; } Canvas component2 = ((Component)val2).GetComponent<Canvas>(); if ((Object)(object)component2 != (Object)null) { val = component2; } val2 = val2.parent; } if ((Object)(object)val == (Object)null) { return false; } Camera val3 = (((int)val.renderMode == 0) ? null : val.worldCamera); if (!RectTransformUtility.RectangleContainsScreenPoint(rect, position, val3)) { return false; } Transform parent = ((Transform)rect).parent; while ((Object)(object)parent != (Object)null) { if ((Object)(object)((Component)parent).GetComponent<RectMask2D>() != (Object)null && !RectTransformUtility.RectangleContainsScreenPoint(((Component)parent).GetComponent<RectTransform>(), position, val3)) { return false; } parent = parent.parent; } return true; } private static ScrollRect ScrollRectAbove(RectTransform rect) { Transform parent = ((Transform)rect).parent; while ((Object)(object)parent != (Object)null) { ScrollRect component = ((Component)parent).GetComponent<ScrollRect>(); if ((Object)(object)component != (Object)null) { return component; } parent = parent.parent; } return null; } private static void BlockScroll(State state, ScrollRect scroll) { if (!((Object)(object)state.Blocked != (Object)null) || !((Object)(object)scroll != (Object)null) || !(((Il2CppObjectBase)state.Blocked).Pointer == ((Il2CppObjectBase)scroll).Pointer)) { if ((Object)(object)state.Blocked != (Object)null) { state.Blocked.scrollSensitivity = state.BlockedSensitivity; } state.Blocked = scroll; if (!((Object)(object)scroll == (Object)null)) { state.BlockedSensitivity = scroll.scrollSensitivity; scroll.scrollSensitivity = 0f; } } } } } namespace RunCustomizer { internal static class DifficultyHooks { private static bool _readingBase; private static float[] _baseGold; private static int Base => Math.Clamp((int)Modes.RunDifficulty, 0, 2); public static void EnemyMultiplierPostfix(ref float __result) { if (Modes.InRun) { __result = Modes.Active.Enemy; } } public static void CycleMultiplierPostfix(ref float __result) { if (Modes.InRun) { __result *= Modes.Active.Enemy / Modes.BaseEnemy[Base]; } } public static void SpawnIntervalPostfix(ref float __result) { if (Modes.InRun) { __result *= Modes.BaseSpawnSpeed[Base] / Modes.Active.SpawnSpeed; } } public static void MinEnemiesPostfix(ref int __result) { if (Modes.InRun) { __result = Math.Max(1, (int)Math.Round((float)__result * Modes.Active.EnemyCount / Modes.BaseEnemyCount[Base])); } } public static bool DeterminePickUpPrefix(PickupSpawner __instance, float pickupChanceMultiplier, float spawningPlayerHealthPercentage, ref NetworkObject __result) { if (!Modes.InRun) { return true; } try { float value = Random.value; float num = 0f; if (HealthPickup.AmountOfHealthPickupsOnMap < __instance.maxAmountOfHealthPickupsOnMap) { num = __instance.healthPickupChanceCurve.Evaluate(spawningPlayerHealthPercentage) * Modes.Active.HealthDrops * pickupChanceMultiplier; } if (num > value) { __result = __instance.healthPickupPrefab; } else if (pickupChanceMultiplier * 0.0016f + num > value && __instance.EnoughXPOrbsToAttract()) { __result = __instance.magnetPickupPrefab; } else { __result = null; } return false; } catch (Exception ex) { RunCustomizerMod.Log.Error("pickup roll failed, using the game's: " + ex.Message); return true; } } public static void GoldMultiplierPostfix(RunGoldCalculatorData __instance, Difficulty difficulty, ref float __result) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) if (!Modes.InRun || _readingBase || difficulty != Modes.RunDifficulty) { return; } try { if (_baseGold == null) { _readingBase = true; _baseGold = new float[3]; for (int i = 0; i < 3; i++) { _baseGold[i] = __instance.GetGoldMultiplierForDifficulty((Difficulty)i); } } float num = Math.Max(0f, Interpolate(_baseGold, Modes.Score(Modes.Active))); bool flag = Math.Max(_baseGold[1], _baseGold[2]) <= 5f; __result = (flag ? ((float)Math.Round(num, 2)) : ((float)Math.Round(num))); } catch (Exception ex) { RunCustomizerMod.Log.Error("gold bonus: " + ex.Message); } finally { _readingBase = false; } } public static float Interpolate(float[] points, float position) { int num = ((!(position < 1f)) ? 1 : 0); return points[num] + (points[num + 1] - points[num]) * (position - (float)num); } } internal static class DifficultyUi { private sealed class Row { public Mode Owner; public Selector Selector; public Func<List<string>> Options; public Func<string> Current; public Action<string> Apply; } private const string ButtonPrefix = "RC_Mode_"; private const string RowsName = "RC_Rows"; private const float RowHeight = 80f; private static readonly Mode[] Buttons = new Mode[2] { Mode.Random, Mode.Custom }; public static readonly Dictionary<Mode, Color> NameColors = new Dictionary<Mode, Color> { [Mode.Random] = new Color(0.84f, 0.48f, 1f), [Mode.Custom] = new Color(0.37f, 0.88f, 0.94f), [Mode.RandomBase] = new Color(0.84f, 0.48f, 1f) }; private static readonly HashSet<IntPtr> SubscribedBaseButtons = new HashSet<IntPtr>(); private static bool _clickingForMode; private static readonly List<Row> Rows = new List<Row>(); private static Mode RandomMode { get { if (!RunCustomizerMod.RandomBaseOnly.Value) { return Mode.Random; } return Mode.RandomBase; } } public static Sprite Icon(Mode mode) { return Sprites.Get(mode switch { Mode.Random => "random.png", Mode.Custom => "custom.png", _ => "randombase.png", }); } private static Mode ButtonOf(Mode mode) { if (mode != Mode.RandomBase) { return mode; } return Mode.Random; } public static void InitializeButtonsPostfix(DifficultiesPanel __instance) { try { Build(__instance); } catch (Exception value) { RunCustomizerMod.Log.Error($"[difficulty ui] {value}"); } } public static void ShowPostfix(DifficultiesPanel __instance) { try { RefreshSelection(__instance); if (Modes.Selected != Mode.Vanilla) { ShowModeDetails(__instance, ButtonOf(Modes.Selected)); } } catch (Exception value) { RunCustomizerMod.Log.Error($"[difficulty ui] {value}"); } } public static void ShowDifficultyDetailsPostfix(DifficultiesPanel __instance) { try { foreach (LocalizeStringEvent item in Localizers(__instance)) { if (!((Object)(object)item == (Object)null) && !((Behaviour)item).enabled) { ((Behaviour)item).enabled = true; item.RefreshString(); } } ShowRows(__instance, null); } catch (Exception value) { RunCustomizerMod.Log.Error($"[difficulty ui] {value}"); } } private static void Build(DifficultiesPanel panel) { List<DifficultySelectionButton> difficultyButtons = panel.difficultyButtons; if (difficultyButtons == null || difficultyButtons.Count == 0) { return; } DifficultySelectionButton val = null; Enumerator<DifficultySelectionButton> enumerator = difficultyButtons.GetEnumerator(); while (enumerator.MoveNext()) { DifficultySelectionButton current = enumerator.Current; if ((Object)(object)current == (Object)null || ((Object)current).name.StartsWith("RC_Mode_")) { continue; } if (val == null) { val = current; } if (!SubscribedBaseButtons.Add(((Il2CppObjectBase)current).Pointer)) { continue; } ((ButtonInteractionHook)current).OnClicked += Action.op_Implicit((Action)delegate { if (!_clickingForMode) { Modes.Selected = Mode.Vanilla; RefreshSelection(panel); RefreshLobbyIcons(); } }); } if ((Object)(object)val == (Object)null) { return; } Transform parent = ((Component)val).transform.parent; if ((Object)(object)parent.Find("RC_Mode_" + Mode.Random) != (Object)null) { return; } Mode[] buttons = Buttons; for (int num = 0; num < buttons.Length; num++) { Mode mode = buttons[num]; GameObject obj = Object.Instantiate<GameObject>(((Component)val).gameObject, parent); ((Object)obj).name = "RC_Mode_" + mode; obj.transform.SetAsLastSibling(); DifficultySelectionButton component = obj.GetComponent<DifficultySelectionButton>(); component._Difficulty_k__BackingField = (Difficulty)(100 + mode); MuteSerializedClicks(obj.GetComponent<Button>()); Sprite sprite = Icon((mode == Mode.Random) ? RandomMode : mode); if ((Object)(object)component.iconImage != (Object)null) { component.iconImage.sprite = sprite; } if ((Object)(object)component.iconImageLocked != (Object)null) { component.iconImageLocked.sprite = sprite; } Mode m = mode; ((ButtonInteractionHook)component).OnClicked += Action.op_Implicit((Action)delegate { Select(panel, (m == Mode.Random) ? RandomMode : m); }); ((ButtonInteractionHook)component).OnHighlight += Action.op_Implicit((Action)delegate { ShowModeDetails(panel, m); }); } BuildRows(panel); RefreshSelection(panel); } private static void Select(DifficultiesPanel panel, Mode mode) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) try { Modes.Selected = mode; Modes.Reroll(); Difficulty item = Modes.Pending.Base; Enumerator<DifficultySelectionButton> enumerator = panel.difficultyButtons.GetEnumerator(); while (enumerator.MoveNext()) { DifficultySelectionButton current = enumerator.Current; if (!((Object)(object)current != (Object)null) || ((Object)current).name.StartsWith("RC_Mode_") || current.Difficulty != item) { continue; } _clickingForMode = true; try { Action onClicked = ((ButtonInteractionHook)current).OnClicked; if (onClicked != null) { onClicked.Invoke(); } } finally { _clickingForMode = false; } break; } RefreshSelection(panel); ShowModeDetails(panel, ButtonOf(mode)); RefreshLobbyIcons(); } catch (Exception value) { RunCustomizerMod.Log.Error($"[difficulty ui] {value}"); } } private static void RefreshSelection(DifficultiesPanel panel) { //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) List<DifficultySelectionButton> difficultyButtons = panel.difficultyButtons; if (difficultyButtons == null) { return; } Mode selected = Modes.Selected; Transform val = null; Enumerator<DifficultySelectionButton> enumerator = difficultyButtons.GetEnumerator(); while (enumerator.MoveNext()) { DifficultySelectionButton current = enumerator.Current; if (!((Object)(object)current == (Object)null) && !((Object)current).name.StartsWith("RC_Mode_")) { if (val == null) { val = ((Component)current).transform.parent; } if (selected != Mode.Vanilla) { ((SelectableButton)current).Deselect(); } else if (current.Difficulty == panel.selectedDifficulty) { ((SelectableButton)current).Select(); } } } if ((Object)(object)val == (Object)null) { return; } Mode[] buttons = Buttons; for (int i = 0; i < buttons.Length; i++) { Mode mode = buttons[i]; Transform obj = val.Find("RC_Mode_" + mode); DifficultySelectionButton val2 = ((obj != null) ? ((Component)obj).GetComponent<DifficultySelectionButton>() : null); if (!((Object)(object)val2 == (Object)null)) { if (selected != Mode.Vanilla && ButtonOf(selected) == mode) { ((SelectableButton)val2).Select(); } else { ((SelectableButton)val2).Deselect(); } } } } private static void ShowModeDetails(DifficultiesPanel panel, Mode mode) { //IL_00c4: Unknown result type (might be due to invalid IL or missing references) try { foreach (LocalizeStringEvent item in Localizers(panel)) { if ((Object)(object)item != (Object)null) { ((Behaviour)item).enabled = false; } } Mode mode2 = ((mode == Mode.Random) ? RandomMode : mode); var (key, key2) = mode2 switch { Mode.Random => ("rn", "rd"), Mode.Custom => ("cn", "cd"), _ => ("bn", "bd"), }; if ((Object)(object)panel.difficultyNameText != (Object)null) { panel.difficultyNameText.text = Strings.Get(key); ((Graphic)panel.difficultyNameText).color = NameColors[mode2]; } SetText(panel.difficultyDescriptionLocalizer, Strings.Get(key2)); SetText(panel.difficultyExplanationLocalizer, (mode2 == Mode.Random) ? RangesText() : ""); LocalizeStringEvent difficultyGoldBonusLocalizer = panel.difficultyGoldBonusLocalizer; if (difficultyGoldBonusLocalizer != null) { ((Component)difficultyGoldBonusLocalizer).gameObject.SetActive(false); } ShowRows(panel, mode); } catch (Exception value) { RunCustomizerMod.Log.Error($"[difficulty ui] {value}"); } } private static string RangesText() { return R("enemy", Modes.RangeEnemy) + " · " + R("spawn", Modes.RangeSpawnSpeed) + "\n" + R("count", Modes.RangeEnemyCount) + " · " + R("health", Modes.RangeHealth); static string R(string key, (float Min, float Max) r) { return $"{Strings.Get(key)} x{r.Min.ToString("0.##", CultureInfo.InvariantCulture)}–{r.Max.ToString("0.##", CultureInfo.InvariantCulture)}"; } } private static void FlipRandomIcon(DifficultiesPanel panel) { Transform val = null; Enumerator<DifficultySelectionButton> enumerator = panel.difficultyButtons.GetEnumerator(); while (enumerator.MoveNext()) { DifficultySelectionButton current = enumerator.Current; if ((Object)(object)current != (Object)null) { val = ((Component)current).transform.parent; break; } } object obj; if (val == null) { obj = null; } else { Transform obj2 = val.Find("RC_Mode_" + Mode.Random); if (obj2 == null) { obj = null; } else { DifficultySelectionButton component = ((Component)obj2).GetComponent<DifficultySelectionButton>(); obj = ((component != null) ? component.iconImage : null); } } Image val2 = (Image)obj; if ((Object)(object)val2 != (Object)null) { MelonCoroutines.Start(Flip(val2, Icon(RandomMode))); } } private static IEnumerator Flip(Image image, Sprite to) { Transform t = ((Component)image).transform; for (float time = 0f; time < 0.12f; time += Time.unscaledDeltaTime) { if (!((Object)(object)image != (Object)null)) { break; } t.localScale = new Vector3(1f - time / 0.12f, 1f, 1f); yield return null; } if ((Object)(object)image == (Object)null) { yield break; } image.sprite = to; for (float time = 0f; time < 0.12f; time += Time.unscaledDeltaTime) { if (!((Object)(object)image != (Object)null)) { break; } float num = time / 0.12f; t.localScale = new Vector3(num, 1f + 0.12f * Mathf.Sin(num * (float)Math.PI), 1f); yield return null; } if ((Object)(object)image != (Object)null) { t.localScale = Vector3.one; } } private static IEnumerable<LocalizeStringEvent> Localizers(DifficultiesPanel p) { return (IEnumerable<LocalizeStringEvent>)(object)new LocalizeStringEvent[4] { p.difficultyNameLocalizer, p.difficultyDescriptionLocalizer, p.difficultyExplanationLocalizer, p.difficultyGoldBonusLocalizer }; } private static void SetText(LocalizeStringEvent loc, string text) { if (!((Object)(object)loc == (Object)null)) { TMP_Text val = ((Component)loc).GetComponent<TMP_Text>() ?? ((Component)loc).GetComponentInChildren<TMP_Text>(true); if ((Object)(object)val != (Object)null) { val.text = text; } } } private static void BuildRows(DifficultiesPanel panel) { //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Expected O, but got Unknown //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) LocalizeStringEvent difficultyExplanationLocalizer = panel.difficultyExplanationLocalizer; Transform val = ((difficultyExplanationLocalizer != null) ? ((Component)difficultyExplanationLocalizer).transform : null); if ((Object)(object)val == (Object)null) { RunCustomizerMod.Log.Warning("[difficulty ui] no details area — settings rows unavailable"); return; } Transform val2 = FindSelectorRow(); if ((Object)(object)val2 == (Object)null) { RunCustomizerMod.Log.Warning("[difficulty ui] no selector row to copy — settings rows unavailable"); return; } GameObject val3 = new GameObject("RC_Rows", (Type[])(object)new Type[1] { Il2CppType.Of<RectTransform>() }); RectTransform component = val3.GetComponent<RectTransform>(); ((Transform)component).SetParent(val, false); Vector2 val4 = default(Vector2); ((Vector2)(ref val4))..ctor(0.5f, 0f); component.anchorMax = val4; component.anchorMin = val4; component.pivot = new Vector2(0.5f, 1f); component.anchoredPosition = new Vector2(0f, -20f); component.sizeDelta = new Vector2(1000f, 320f); VerticalLayoutGroup obj = val3.AddComponent<VerticalLayoutGroup>(); ((LayoutGroup)obj).childAlignment = (TextAnchor)1; ((HorizontalOrVerticalLayoutGroup)obj).childControlHeight = false; ((HorizontalOrVerticalLayoutGroup)obj).childControlWidth = false; ((HorizontalOrVerticalLayoutGroup)obj).childForceExpandHeight = false; ((HorizontalOrVerticalLayoutGroup)obj).childForceExpandWidth = false; Rows.RemoveAll((Row r) => (Object)(object)r.Selector == (Object)null); List<Selectable> list = new List<Selectable> { AddRow(val3.transform, val2, Mode.Random, "kind", new Row { Options = () => new List<string> { Strings.Get("kind_all"), Strings.Get("kind_base") }, Current = () => Strings.Get(RunCustomizerMod.RandomBaseOnly.Value ? "kind_base" : "kind_all"), Apply = delegate(string option) { bool flag = option == Strings.Get("kind_base"); if (RunCustomizerMod.RandomBaseOnly.Value != flag) { RunCustomizerMod.RandomBaseOnly.Value = flag; RunCustomizerMod.Save(); Mode selected = Modes.Selected; if ((selected == Mode.Random || selected == Mode.RandomBase) ? true : false) { Modes.Selected = RandomMode; Modes.SyncLobby(); RefreshLobbyIcons(); } ShowModeDetails(panel, Mode.Random); FlipRandomIcon(panel); } } }) }; List<Selectable> list2 = new List<Selectable>(); list2.Add(AddValueRow(val3.transform, val2, "enemy", RunCustomizerMod.CustomEnemy, new float[16] { 0.5f, 0.75f, 1f, 1.1f, 1.25f, 1.5f, 1.75f, 2f, 2.05f, 2.25f, 2.5f, 2.75f, 3f, 3.5f, 4f, 4.5f })); list2.Add(AddValueRow(val3.transform, val2, "spawn", RunCustomizerMod.CustomSpawnSpeed, new float[9] { 0.75f, 1f, 1.25f, 1.43f, 1.75f, 2f, 2.5f, 3f, 4f })); list2.Add(AddValueRow(val3.transform, val2, "count", RunCustomizerMod.CustomEnemyCount, new float[6] { 0.75f, 1f, 1.25f, 1.5f, 1.75f, 2f })); list2.Add(AddValueRow(val3.transform, val2, "health", RunCustomizerMod.CustomHealth, new float[7] { 0.1f, 0.25f, 0.5f, 0.75f, 1f, 1.25f, 1.5f })); List<Selectable> list3 = list2; LinkVertically(list); LinkVertically(list3); RegisterInputReceivers(panel, list, list3); val3.SetActive(false); } private static Selectable AddValueRow(Transform container, Transform template, string labelKey, MelonPreferences_Entry<float> entry, float[] values) { return AddRow(container, template, Mode.Custom, labelKey, new Row { Options = delegate { List<float> list = new List<float>(values); if (!list.Contains(entry.Value)) { list.Add(entry.Value); list.Sort(); } return list.ConvertAll<string>(Format); }, Current = () => Format(entry.Value), Apply = delegate(string option) { if (float.TryParse(option.TrimStart('x'), NumberStyles.Float, CultureInfo.InvariantCulture, out var result) && entry.Value != result) { entry.Value = result; RunCustomizerMod.Save(); if (Modes.Selected == Mode.Custom) { Modes.Reroll(); } } } }); } private static Transform FindSelectorRow() { foreach (Selector item in Resources.FindObjectsOfTypeAll<Selector>()) { Transform parent = ((Component)item).transform.parent; if ((Object)(object)parent != (Object)null && (Object)(object)((Component)parent).GetComponentInChildren<TMP_Text>(true) != (Object)null && !((Object)parent).name.StartsWith("RC")) { return parent; } } return null; } private static Selectable AddRow(Transform container, Transform template, Mode owner, string labelKey, Row row) { //IL_0048: Unknown result type (might be due to invalid IL or missing references) GameObject obj = Object.Instantiate<GameObject>(((Component)template).gameObject, container); ((Object)obj).name = "RC_" + labelKey; obj.SetActive(true); StripGameLogic(obj); obj.GetComponent<RectTransform>().sizeDelta = new Vector2(1000f, 80f); Selector componentInChildren = obj.GetComponentInChildren<Selector>(true); bool flag = false; foreach (TMP_Text componentsInChild in obj.GetComponentsInChildren<TMP_Text>(true)) { if (componentsInChild.transform.IsChildOf(((Component)componentInChildren).transform)) { continue; } if (flag) { Object.Destroy((Object)(object)((Component)componentsInChild).gameObject); continue; } flag = true; foreach (LocalizeStringEvent component in ((Component)componentsInChild).GetComponents<LocalizeStringEvent>()) { Object.DestroyImmediate((Object)(object)component); } Strings.Bind(componentsInChild, labelKey); } componentInChildren.localizationMode = (LocalizationMode)2; if ((Object)(object)componentInChildren.valueLocalizeString != (Object)null) { ((Behaviour)componentInChildren.valueLocalizeString).enabled = false; } AddArrow(componentInChildren, componentInChildren.previousButton, "<"); AddArrow(componentInChildren, componentInChildren.nextButton, ">"); row.Owner = owner; row.Selector = componentInChildren; componentInChildren.OnOptionSelected += Action<string>.op_Implicit((Action<string>)delegate(string option) { try { row.Apply(option); } catch (Exception value) { RunCustomizerMod.Log.Error($"[difficulty ui] {value}"); } }); Rows.Add(row); Show(row); return (Selectable)(object)componentInChildren; } private static void AddArrow(Selector selector, Button button, string glyph) { //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)button == (Object)null || (Object)(object)selector.valueText == (Object)null) { return; } GameObject val = Object.Instantiate<GameObject>(((Component)selector.valueText).gameObject, ((Component)button).transform); ((Object)val).name = "RC_Arrow"; foreach (LocalizeStringEvent component2 in val.GetComponents<LocalizeStringEvent>()) { Object.DestroyImmediate((Object)(object)component2); } TMP_Text component = val.GetComponent<TMP_Text>(); component.text = glyph; component.alignment = (TextAlignmentOptions)514; ((Graphic)component).raycastTarget = false; RectTransform rectTransform = component.rectTransform; rectTransform.anchorMin = Vector2.zero; rectTransform.anchorMax = Vector2.one; Vector2 offsetMin = (rectTransform.offsetMax = Vector2.zero); rectTransform.offsetMin = offsetMin; Transform transform = ((Component)button).transform; if ((transform.lossyScale.x < 0f) ^ (transform.right.x < 0f)) { ((Transform)rectTransform).localScale = new Vector3(-1f, 1f, 1f); } } private static void Show(Row row) { List<string> val = new List<string>(); foreach (string item in row.Options()) { val.Add(item); } string text = row.Current(); row.Selector.SetOptions(val); row.Selector.SetSelectedOption(text); row.Selector.SetNonLocalizedValueText(text); } private static string Format(float v) { return "x" + v.ToString("0.##", CultureInfo.InvariantCulture); } private static void ShowRows(DifficultiesPanel panel, Mode? owner) { LocalizeStringEvent difficultyExplanationLocalizer = panel.difficultyExplanationLocalizer; Transform val = ((difficultyExplanationLocalizer != null) ? ((Component)difficultyExplanationLocalizer).transform.Find("RC_Rows") : null); if ((Object)(object)val == (Object)null) { return; } bool flag = false; foreach (Row row in Rows) { if (!((Object)(object)row.Selector == (Object)null)) { bool flag2 = owner == row.Owner; ((Component)((Component)row.Selector).transform.parent).gameObject.SetActive(flag2); if (flag2) { flag = true; Show(row); } } } ((Component)val).gameObject.SetActive(flag); if (flag) { RandomWizard.AllowClicks(val, ((Component)panel).transform); } } public static void LobbyIconPostfix(DifficultySelectionDisplayer __instance) { try { if (Modes.Selected != Mode.Vanilla && Modes.IsHost && (Object)(object)__instance.iconImage != (Object)null) { __instance.iconImage.sprite = Icon(Modes.Selected); } } catch (Exception value) { RunCustomizerMod.Log.Error($"[lobby icon] {value}"); } } private static void RefreshLobbyIcons() { //IL_0029: Unknown result type (might be due to invalid IL or missing references) MainMenuManager instance = NetworkSingleton<MainMenuManager>.Instance; if ((Object)(object)instance == (Object)null) { return; } foreach (DifficultySelectionDisplayer item in Object.FindObjectsOfType<DifficultySelectionDisplayer>()) { item.DisplayDifficulty(instance.CurrentDifficulty.Value); } } public static void RunIconPostfix(DifficultyLevelVisualizer __instance) { try { if (Modes.InRun && (Object)(object)__instance.iconImage != (Object)null) { __instance.iconImage.sprite = Icon(Modes.Selected); } } catch (Exception value) { RunCustomizerMod.Log.Error($"[run icon] {value}"); } } private static void LinkVertically(List<Selectable> rows) { for (int i = 0; i < rows.Count; i++) { Navigation navigation = rows[i].navigation; navigation.mode = (Mode)4; navigation.selectOnUp = ((i > 0) ? rows[i - 1] : null); navigation.selectOnDown = ((i < rows.Count - 1) ? rows[i + 1] : null); navigation.selectOnLeft = null; navigation.selectOnRight = null; rows[i].navigation = navigation; } } private static void RegisterInputReceivers(DifficultiesPanel panel, params List<Selectable>[] groups) { //IL_0077: Unknown result type (might be due to invalid IL or missing references) UIPanel val = ((Component)panel).GetComponent<UIPanel>() ?? ((Component)panel).GetComponentInChildren<UIPanel>(true); if ((Object)(object)val == (Object)null) { RunCustomizerMod.Log.Warning("[difficulty ui] no UIPanel — arrow keys won't change values"); return; } UIPanel val2 = val; if (val2.inputReceivers == null) { List<IUIInputReceiver> val3 = (val2.inputReceivers = new List<IUIInputReceiver>()); } for (int i = 0; i < groups.Length; i++) { foreach (Selectable item in groups[i]) { val.inputReceivers.Add((IUIInputReceiver)(((object)((Il2CppObjectBase)item).TryCast<IUIInputReceiver>()) ?? ((object)new IUIInputReceiver(((Il2CppObjectBase)item).Pointer)))); } } } private static void StripGameLogic(GameObject row) { foreach (MonoBehaviour component in row.GetComponents<MonoBehaviour>()) { if (((MemberInfo)((Object)component).GetIl2CppType()).Name != "Image") { Object.DestroyImmediate((Object)(object)component); } } foreach (TextReplacer componentsInChild in row.GetComponentsInChildren<TextReplacer>(true)) { Object.DestroyImmediate((Object)(object)componentsInChild); } } private static void MuteSerializedClicks(Button button) { if (!((Object)(object)button == (Object)null)) { for (int i = 0; i < ((UnityEventBase)button.onClick).GetPersistentEventCount(); i++) { ((UnityEventBase)button.onClick).SetPersistentListenerState(i, (UnityEventCallState)0); } } } } internal static class MenuPage { private static Mode RandomMode { get { if (!RunCustomizerMod.RandomBaseOnly.Value) { return Mode.Random; } return Mode.RandomBase; } } [MethodImpl(MethodImplOptions.NoInlining)] public static void Register() { Menu.AddPage("RunCustomizer", "Run Customizer").Choice((Func<string>)(() => Strings.Get("difficulty")), (Func<IList<string>>)(() => new string[3] { Strings.Get("standard"), Strings.Get("rn"), Strings.Get("cn") }), (Func<int>)(() => Modes.Selected switch { Mode.Vanilla => 0, Mode.Custom => 2, _ => 1, }), (Action<int>)delegate(int i) { Modes.Selected = i switch { 0 => Mode.Vanilla, 2 => Mode.Custom, _ => RandomMode, }; }, (int?)0).Choice((Func<string>)(() => Strings.Get("kind")), (Func<IList<string>>)(() => new string[2] { Strings.Get("kind_all"), Strings.Get("kind_base") }), (Func<int>)(() => RunCustomizerMod.RandomBaseOnly.Value ? 1 : 0), (Action<int>)delegate(int i) { bool flag = i == 1; if (RunCustomizerMod.RandomBaseOnly.Value != flag) { RunCustomizerMod.RandomBaseOnly.Value = flag; RunCustomizerMod.Save(); Mode selected = Modes.Selected; if ((selected == Mode.Random || selected == Mode.RandomBase) ? true : false) { Modes.Selected = RandomMode; } } }, (int?)(RunCustomizerMod.RandomBaseOnly.DefaultValue ? 1 : 0)) .Header((Func<string>)(() => Strings.Get("cn"))) .Number((Func<string>)(() => Strings.Get("enemy")), 0.1f, 10f, 0.05f, (Func<float>)(() => RunCustomizerMod.CustomEnemy.Value), (Action<float>)delegate(float v) { Set(RunCustomizerMod.CustomEnemy, v); }, (Func<float, string>)Multiplier, (float?)RunCustomizerMod.CustomEnemy.DefaultValue) .Number((Func<string>)(() => Strings.Get("spawn")), 0.1f, 10f, 0.05f, (Func<float>)(() => RunCustomizerMod.CustomSpawnSpeed.Value), (Action<float>)delegate(float v) { Set(RunCustomizerMod.CustomSpawnSpeed, v); }, (Func<float, string>)Multiplier, (float?)RunCustomizerMod.CustomSpawnSpeed.DefaultValue) .Number((Func<string>)(() => Strings.Get("count")), 0.1f, 10f, 0.05f, (Func<float>)(() => RunCustomizerMod.CustomEnemyCount.Value), (Action<float>)delegate(float v) { Set(RunCustomizerMod.CustomEnemyCount, v); }, (Func<float, string>)Multiplier, (float?)RunCustomizerMod.CustomEnemyCount.DefaultValue) .Number((Func<string>)(() => Strings.Get("health")), 0f, 10f, 0.05f, (Func<float>)(() => RunCustomizerMod.CustomHealth.Value), (Action<float>)delegate(float v) { Set(RunCustomizerMod.CustomHealth, v); }, (Func<float, string>)Multiplier, (float?)RunCustomizerMod.CustomHealth.DefaultValue); } private static string Multiplier(float v) { return "x" + v.ToString("0.##", CultureInfo.InvariantCulture); } private static void Set(MelonPreferences_Entry<float> entry, float value) { if (entry.Value != value) { entry.Value = value; RunCustomizerMod.Save(); } } } public class RunCustomizerMod : MelonMod { public static Instance Log; private static MelonPreferences_Category _category; internal static MelonPreferences_Entry<int> ModeEntry; internal static MelonPreferences_Entry<float> CustomEnemy; internal static MelonPreferences_Entry<float> CustomSpawnSpeed; internal static MelonPreferences_Entry<float> CustomEnemyCount; internal static MelonPreferences_Entry<float> CustomHealth; internal static MelonPreferences_Entry<bool> RandomBaseOnly; private int _hooksInstalled; private int _hooksTotal; private float _nextLobbySync; public override void OnInitializeMelon() { Log = ((MelonBase)this).LoggerInstance; _category = MelonPreferences.CreateCategory("RunCustomizer", "Run Customizer"); ModeEntry = _category.CreateEntry<int>("DifficultyMode", 0, "Difficulty Mode", "0 = обычная (выбор игры), 1 = случайная, 2 = своя, 3 = случайная из базовых", false, false, (ValueValidator)(object)new ValueRange<int>(0, 3), (string)null); CustomEnemy = _category.CreateEntry<float>("CustomEnemyStrength", 2.05f, "Custom: Enemy Strength", "Своя сложность: здоровье и урон врагов (Normal 1.1, Hard 2.05, Nightmare 3)", false, false, (ValueValidator)(object)new ValueRange<float>(0.1f, 10f), (string)null); CustomSpawnSpeed = _category.CreateEntry<float>("CustomSpawnSpeed", 1.43f, "Custom: Spawn Speed", "Своя сложность: скорость появления врагов (Normal 1, Hard 1.43, Nightmare 2.5)", false, false, (ValueValidator)(object)new ValueRange<float>(0.1f, 10f), (string)null); CustomEnemyCount = _category.CreateEntry<float>("CustomEnemyCount", 1.25f, "Custom: Enemy Count", "Своя сложность: минимум врагов на карте (Normal 1, Hard 1.25, Nightmare 1.5)", false, false, (ValueValidator)(object)new ValueRange<float>(0.1f, 10f), (string)null); CustomHealth = _category.CreateEntry<float>("CustomHealthDrops", 0.5f, "Custom: Health Drops", "Своя сложность: шанс аптечек (Normal 1, Hard 0.5, Nightmare 0.25)", false, false, (ValueValidator)(object)new ValueRange<float>(0f, 10f), (string)null); RandomBaseOnly = _category.CreateEntry<bool>("RandomBaseOnly", false, "Random: Base Difficulty Only", "Случайная сложность: false — случайно каждый параметр, true — случайно одна из Normal/Hard/Nightmare", false, false, (ValueValidator)null, (string)null); _category.SaveToFile(false); ApplyPatches(); Log.Msg($"{_hooksInstalled}/{_hooksTotal} hooks — difficulty mode: {Modes.Selected}"); MatchmakingGuard.Register(((MelonBase)this).HarmonyInstance, Log, "Run Customizer", () => (Modes.Selected != Mode.Vanilla) ? string.Format(Strings.Get("mm"), Strings.Get((Modes.Selected == Mode.Custom) ? "cn" : "rn")) : null); if (MelonBase.FindMelon("Mod Menu", "Relsev") != null) { try { MenuPage.Register(); } catch (Exception ex) { Log.Warning("Mod Menu page: " + ex.Message); } } } internal static void Save() { _category.SaveToFile(false); } public override void OnSceneWasInitialized(int buildIndex, string sceneName) { if ((sceneName == "MainMenu" || sceneName == "StartMenu") ? true : false) { Modes.OnBackToMenu(); } } public override void OnUpdate() { WheelSelect.Tick(); MatchmakingGuard.Tick(); if (Time.unscaledTime < _nextLobbySync) { return; } _nextLobbySync = Time.unscaledTime + 0.5f; try { Modes.SyncLobby(); } catch (Exception ex) { Log.Error("lobby sync: " + ex.Message); } } private void ApplyPatches() { Patch(Method(typeof(EnemyDifficultyParameters), "GetMultiplierForDifficulty"), null, "EnemyMultiplierPostfix"); Patch(Method(typeof(EnemyDifficultyParameters), "GetExtraDifficultyMultiplierForCycle"), null, "CycleMultiplierPostfix"); Patch(AccessTools.PropertyGetter(typeof(WaveConfiguration), "SpawnIntervalInSecondsForDifficultyAndCycle"), null, "SpawnIntervalPostfix"); Patch(AccessTools.PropertyGetter(typeof(WaveConfiguration), "MinNumberOfConcurrentEnemiesForDifficulty"), null, "MinEnemiesPostfix"); Patch(Method(typeof(PickupSpawner), "DeterminePickUp"), "DeterminePickUpPrefix"); Patch(Method(typeof(RunGoldCalculatorData), "GetGoldMultiplierForDifficulty"), null, "GoldMultiplierPostfix"); Patch(Method(typeof(MainMenuManager), "PrepareForLevelStart"), null, "OnLevelStartPostfix", typeof(RunCustomizerMod)); Patch(Method(typeof(DifficultiesPanel), "InitializeButtons"), null, "InitializeButtonsPostfix", typeof(DifficultyUi)); Patch(Method(typeof(DifficultiesPanel), "Show"), null, "ShowPostfix", typeof(DifficultyUi)); Patch(Method(typeof(DifficultiesPanel), "ShowDifficultyDetails"), null, "ShowDifficultyDetailsPostfix", typeof(DifficultyUi)); Patch(Method(typeof(DifficultySelectionDisplayer), "DisplayDifficulty"), null, "LobbyIconPostfix", typeof(DifficultyUi)); Patch(Method(typeof(DifficultyLevelVisualizer), "Start"), null, "RunIconPostfix", typeof(DifficultyUi)); Patch(Method(typeof(MissionStatsPanel), "Show"), null, "MissionStatsPostfix", typeof(StatsUi)); Patch(Method(typeof(GameOverMissionStatsPanel), "SetValues"), null, "GameOverStatsPostfix", typeof(StatsUi)); Patch(Method(typeof(CharacterSelector), "InitializeCharacterButtons"), null, "InitializePostfix", typeof(RandomWizard)); Patch(Method(typeof(CharacterPanel), "OnSelected"), "PanelSelectedPrefix", null, typeof(RandomWizard)); Patch(Method(typeof(CharacterPanel), "OnHighlightStarted"), "PanelHighlightPrefix", null, typeof(RandomWizard)); Patch(Method(typeof(CharacterSelector), "MakeInitialSelection"), null, "InitialSelectionPostfix", typeof(RandomWizard)); Patch(Method(typeof(CharacterPanel), "Display"), null, "DisplayPostfix", typeof(RandomWizard)); Patch(Method(typeof(MainMenuManager), "StartLevelOnServerRpc"), "StartLevelPrefix", null, typeof(RandomWizard)); Patch(Method(typeof(MainMenuManager), "PrepareForLevelStart"), "PrepareForLevelStartPrefix", null, typeof(RandomWizard)); } private static void OnLevelStartPostfix() { try { Modes.OnLevelStart(); } catch (Exception value) { Log.Error($"level start: {value}"); } } private static MethodInfo Method(Type type, string name) { return AccessTools.Method(type, name, (Type[])null, (Type[])null); } private void Patch(MethodInfo original, string prefix = null, string postfix = null, Type patchClass = null) { _hooksTotal++; if (Hooks.Patch(((MelonBase)this).HarmonyInstance, Log, original, patchClass ?? typeof(DifficultyHooks), prefix, postfix)) { _hooksInstalled++; } } } internal enum Mode { Vanilla, Random, Custom, RandomBase } internal struct Params { public float Enemy; public float SpawnSpeed; public float EnemyCount; public float HealthDrops; public override string ToString() { return $"enemies x{Enemy:0.##}, spawn speed x{SpawnSpeed:0.##}, enemy count x{EnemyCount:0.##}, health drops x{HealthDrops:0.##}"; } } internal static class Modes { public static readonly float[] BaseEnemy = new float[3] { 1.1f, 2.05f, 3f }; public static readonly float[] BaseSpawnSpeed = new float[3] { 1f, 1.4285715f, 2.5f }; public static readonly float[] BaseEnemyCount = new float[3] { 1f, 1.25f, 1.5f }; public static readonly float[] BaseHealth = new float[3] { 1f, 0.5f, 0.25f }; public static readonly (float Min, float Max) RangeEnemy = (Min: 0.5f, Max: 4.5f); public static readonly (float Min, float Max) RangeSpawnSpeed = (Min: 0.75f, Max: 4f); public static readonly (float Min, float Max) RangeEnemyCount = (Min: 0.75f, Max: 2f); public static readonly (float Min, float Max) RangeHealth = (Min: 0.1f, Max: 1.5f); private static (Params Values, Difficulty Base)? _pending; private static bool _inLevel; public static Mode Selected { get { return (Mode)Math.Clamp(RunCustomizerMod.ModeEntry.Value, 0, 3); } set { if (Selected != value) { RunCustomizerMod.ModeEntry.Value = (int)value; RunCustomizerMod.Save(); _pending = null; } } } public static Params Custom => new Params { Enemy = RunCustomizerMod.CustomEnemy.Value, SpawnSpeed = RunCustomizerMod.CustomSpawnSpeed.Value, EnemyCount = RunCustomizerMod.CustomEnemyCount.Value, HealthDrops = RunCustomizerMod.CustomHealth.Value }; public static bool InRun { get; private set; } public static Params Active { get; private set; } public static (Params Values, Difficulty Base) Pending { get { (Params, Difficulty) valueOrDefault = _pending.GetValueOrDefault(); if (!_pending.HasValue) { valueOrDefault = Roll(); _pending = valueOrDefault; return valueOrDefault; } return valueOrDefault; } } public static bool IsHost { get { NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton != (Object)null) { return singleton.IsServer; } return false; } } public static Mode LastRunMode { get; private set; } public static Difficulty RunDifficulty { get { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) try { RunData instance = SingletonPersistent<RunData>.Instance; return (Difficulty)((instance != null) ? ((int)instance.SelectedDifficulty) : 0); } catch { return (Difficulty)0; } } } public static void Reroll() { _pending = null; } private static (Params, Difficulty) Roll() { //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) Random random = new Random(); switch (Selected) { case Mode.Random: { Params obj = new Params { Enemy = Pick(random, RangeEnemy), SpawnSpeed = Pick(random, RangeSpawnSpeed), EnemyCount = Pick(random, RangeEnemyCount), HealthDrops = Pick(random, RangeHealth) }; return (obj, NearestBase(obj)); } case Mode.Custom: return (Custom, NearestBase(Custom)); case Mode.RandomBase: { int num = HighestUnlocked(); Difficulty val = (Difficulty)random.Next(0, num + 1); return (Of(val), val); } default: return (Of((Difficulty)0), (Difficulty)0); } } private static float Pick(Random rnd, (float Min, float Max) r) { return (float)Math.Round((double)r.Min + rnd.NextDouble() * (double)(r.Max - r.Min), 2); } public static Params Of(Difficulty d) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Expected I4, but got Unknown int num = (int)d; return new Params { Enemy = BaseEnemy[num], SpawnSpeed = BaseSpawnSpeed[num], EnemyCount = BaseEnemyCount[num], HealthDrops = BaseHealth[num] }; } public static float Score(Params p) { return (Position(p.Enemy, BaseEnemy) + Position(p.SpawnSpeed, BaseSpawnSpeed) + Position(p.EnemyCount, BaseEnemyCount) + Position(p.HealthDrops, BaseHealth)) / 4f; } private static float Position(float v, float[] bases) { int num = (((bases[2] > bases[0]) ? (!(v < bases[1])) : (!(v > bases[1]))) ? 1 : 0); float num2 = bases[num]; float num3 = bases[num + 1]; return (float)num + (v - num2) / (num3 - num2); } private static Difficulty NearestBase(Params p) { return (Difficulty)Math.Clamp((int)Math.Round(Score(p)), 0, HighestUnlocked()); } public static int HighestUnlocked() { int result = 0; for (int i = 1; i <= 2; i++) { try { DifficultyConfiguration val = DifficultyConfigurationRepository.Get((Difficulty)i); if ((Object)(object)val != (Object)null && !val.IsLockedByProgression()) { result = i; } } catch { } } return result; } public static void OnLevelStart() { //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) InRun = false; _inLevel = true; LastRunMode = Mode.Vanilla; if (Selected != Mode.Vanilla && IsHost) { Params item = Pending.Values; _pending = null; LastRunMode = Selected; if (Selected == Mode.RandomBase) { RunCustomizerMod.Log.Msg($"run difficulty: {RunDifficulty} (random from base)"); return; } Active = item; InRun = true; RunCustomizerMod.Log.Msg($"run difficulty ({Selected}, base {RunDifficulty}): {item}"); } } public static void OnBackToMenu() { InRun = (_inLevel = false); } public static void SyncLobby() { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) if (_inLevel || Selected == Mode.Vanilla || !IsHost) { return; } MainMenuManager instance = NetworkSingleton<MainMenuManager>.Instance; if ((Object)(object)instance == (Object)null) { return; } Difficulty item = Pending.Base; if (instance.CurrentDifficulty.Value != item) { instance.SetDifficulty(item); RunSetupPreferences instance2 = SingletonPersistent<RunSetupPreferences>.Instance; if (instance2 != null) { instance2.SetPreferredDifficulty(item); } } } } internal static class RandomWizard { private enum Kind { Instant, Surprise } private static readonly Dictionary<Kind, string> Names = new Dictionary<Kind, string> { [Kind.Instant] = "RC_RandomWizard", [Kind.Surprise] = "RC_SurpriseWizard" }; private static bool _pendingSurprise; private static Kind? _shown; private static bool _cycling; private static bool _rolling; private static bool _seatCycling; private static int _appliedFrame = -1; private static Color? _aliasColor; private static readonly List<SkinId> Skins = new List<SkinId>(); private static readonly Random Rnd = new Random(); private static int _lastSkin = -1; private static bool IsOurs(CharacterButton b) { if ((Object)(object)b != (Object)null) { return ((Object)b).name.StartsWith("RC_"); } return false; } public static void InitializePostfix(CharacterSelector __instance) { try { AddButtons(__instance); } catch (Exception value) { RunCustomizerMod.Log.Error($"[random wizard] {value}"); } } private static void AddButtons(CharacterSelector selector) { //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_029d: Unknown result type (might be due to invalid IL or missing references) //IL_02a2: Unknown result type (might be due to invalid IL or missing references) //IL_02a6: Unknown result type (might be due to invalid IL or missing references) //IL_02ab: Unknown result type (might be due to invalid IL or missing references) //IL_02ad: Unknown result type (might be due to invalid IL or missing references) //IL_02bb: Unknown result type (might be due to invalid IL or missing references) //IL_02d0: Unknown result type (might be due to invalid IL or missing references) //IL_02d7: Unknown result type (might be due to invalid IL or missing references) List<CharacterButton> initializedCharacterButtons = selector.initializedCharacterButtons; if (initializedCharacterButtons == null || initializedCharacterButtons.Count == 0) { return; } Skins.Clear(); Enumerator<CharacterButton> enumerator = initializedCharacterButtons.GetEnumerator(); while (enumerator.MoveNext()) { CharacterButton current = enumerator.Current; if ((Object)(object)((current != null) ? current.resource : null) != (Object)null && !current.resource.Disabled) { Skins.Add(current.resource.DefaultSkin); } } CharacterButton obj = RandomCandidate(selector, null); CharacterResource val = ((obj != null) ? obj.resource : null); if ((Object)(object)val == (Object)null) { return; } CharacterButton val2 = initializedCharacterButtons[initializedCharacterButtons.Count - 1]; Transform parent = ((Component)val2).transform.parent; int num = 0; enumerator = initializedCharacterButtons.GetEnumerator(); while (enumerator.MoveNext()) { CharacterButton current2 = enumerator.Current; if ((Object)(object)current2 != (Object)null && (Object)(object)((Component)current2).transform.parent == (Object)(object)parent) { num = Math.Max(num, ((Component)current2).transform.GetSiblingIndex()); } } Kind[] array = new Kind[2] { Kind.Instant, Kind.Surprise }; foreach (Kind kind in array) { CharacterButton val3 = Find(selector, kind); if ((Object)(object)val3 == (Object)null) { GameObject obj2 = Object.Instantiate<GameObject>(((Component)val2).gameObject, parent); ((Object)obj2).name = Names[kind]; obj2.transform.SetSiblingIndex(++num); val3 = obj2.GetComponent<CharacterButton>(); Selectable component = obj2.GetComponent<Selectable>(); if ((Object)(object)component != (Object)null) { Navigation navigation = component.navigation; navigation.mode = (Mode)3; component.navigation = navigation; } Kind k = kind; val3.OnSelected += Action.op_Implicit((Action)delegate { OnClicked(selector, k); }); ButtonEventHook eventHook = val3.eventHook; if (eventHook != null) { eventHook.OnHighlightStarted += Action.op_Implicit((Action)delegate { try { Show(PanelOf(selector), k); } catch (Exception value) { RunCustomizerMod.Log.Error($"[random wizard] {value}"); } }); } for (int num2 = parent.childCount - 1; num2 >= 0; num2--) { Transform child = parent.GetChild(num2); if (((Component)child).gameObject.activeSelf && (Object)(object)((Component)child).GetComponent<RectTransform>() != (Object)null && (Object)(object)((Component)child).GetComponent<CharacterButton>() == (Object)null) { ((Component)child).gameObject.SetActive(false); break; } } } ((Component)val3).gameObject.SetActive(true); val3.Initialize(val); Image iconImage = val3.iconImage; if ((Object)(object)iconImage != (Object)null) { Rect rect = ((Graphic)iconImage).rectTransform.rect; Vector2 size = ((Rect)(ref rect)).size; float aspect = ((size.x > 1f && size.y > 1f) ? (size.x / size.y) : 0f); iconImage.sprite = Sprites.Get((kind == Kind.Instant) ? "random_wizard.png" : "surprise_wizard.png", aspect); iconImage.preserveAspect = false; } if ((Object)(object)val3.extraGoldBonusPanel != (Object)null) { val3.extraGoldBonusPanel.SetActive(false); } } ShowSelected(selector, _pendingSurprise); } private static void OnClicked(CharacterSelector selector, Kind kind) { //IL_0064: Unknown result type (might be due to invalid IL or missing references) try { if (_rolling) { return; } CharacterPanel panel = PanelOf(selector); if (kind == Kind.Surprise) { _pendingSurprise = true; CharacterSelectorSoundPlayer soundPlayer = selector.soundPlayer; if (soundPlayer != null) { soundPlayer.PlayCharacterSelectSound(); } ShowSelected(selector, surpriseSelected: true); Show(panel, Kind.Surprise); if (!_seatCycling) { MelonCoroutines.Start(CycleSeatModel()); } } else { LocalPlayer instance = SingletonPersistent<LocalPlayer>.Instance; CharacterId? exclude = ((instance != null) ? new CharacterId?(instance.GetSelectedCharacter()) : ((CharacterId?)null)); CharacterButton val = RandomCandidate(selector, exclude); if ((Object)(object)val != (Object)null) { MelonCoroutines.Start(Roulette(selector, panel, val)); } } } catch (Exception value) { RunCustomizerMod.Log.Error($"[random wizard] {value}"); } } public static void PanelHighlightPrefix() { if (!_rolling) { _shown = null; } } public static void PanelSelectedPrefix(CharacterPanel __instance) { if (_rolling) { return; } _shown = null; if (_pendingSurprise) { _pendingSurprise = false; CharacterSelector selector = __instance.selector; if ((Object)(object)selector != (Object)null) { ShowSelected(selector, surpriseSelected: false); } } } public static void InitialSelectionPostfix(CharacterSelector __instance) { if (_pendingSurprise) { ShowSelected(__instance, surpriseSelected: true); Show(PanelOf(__instance), Kind.Surprise); } } private static void ShowSelected(CharacterSelector selector, bool surpriseSelected) { CharacterButton val = Find(selector, Kind.Instant); if ((Object)(object)((val != null) ? val.animator : null) != (Object)null) { val.animator.SetBool(CharacterButton.IsSelected, false); } CharacterButton val2 = Find(selector, Kind.Surprise); if ((Object)(object)((val2 != null) ? val2.animator : null) != (Object)null) { val2.animator.SetBool(CharacterButton.IsSelected, surpriseSelected); } CharacterButton selectedCharacterButton = selector.selectedCharacterButton; if ((Object)(object)((selectedCharacterButton != null) ? selectedCharacterButton.animator : null) != (Object)null && !IsOurs(selectedCharacterButton)) { selectedCharacterButton.animator.SetBool(CharacterButton.IsSelected, !surpriseSelected); } } public static void DisplayPostfix(CharacterPanel __instance, bool isLocked) { try { if (_shown.HasValue) { Show(__instance, _shown.Value); } else { Restore(__instance, isLocked); } } catch (Exception value) { RunCustomizerMod.Log.Error($"[random wizard] {value}"); } } private static void Show(CharacterPanel panel, Kind kind) { //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)panel == (Object)null) { return; } _shown = kind; LoreAndDetailsDisplayToggler loreAndDetailsDisplayToggler = panel.loreAndDetailsDisplayToggler; if ((Object)(object)loreAndDetailsDisplayToggler != (Object)null) { loreAndDetailsDisplayToggler.ToggleLoreDisplay(true); ButtonPrompt toggleLoreDisplayPrompt = loreAndDetailsDisplayToggler.toggleLoreDisplayPrompt; if (toggleLoreDisplayPrompt != null) { ((Component)toggleLoreDisplayPrompt).gameObject.SetActive(false); } } CharacterLoreDisplay characterLoreDisplay = panel.characterLoreDisplay; bool flag = kind == Kind.Surprise && _pendingSurprise; CharacterInfoDisplay characterInfoDisplay = panel.characterInfoDisplay; object loc; if (characterInfoDisplay == null) { loc = null; } else { CharacterUIVisualizer characterVisualizer = characterInfoDisplay.characterVisualizer; loc = ((characterVisualizer != null) ? characterVisualizer.nameLocalizer : null); } SetText((LocalizeStringEvent)loc, Strings.Get((kind == Kind.Instant) ? "rw" : "sw")); SetText((characterLoreDisplay != null) ? characterLoreDisplay.aliasLocalizer : null, Strings.Get(flag ? "w_chosen" : "w_alias")); object obj; if (characterLoreDisplay == null) { obj = null; } else { LocalizeStringEvent aliasLocalizer = characterLoreDisplay.aliasLocalizer; obj = ((aliasLocalizer != null) ? ((Component)aliasLocalizer).GetComponent<TMP_Text>() : null); } TMP_Text val = (TMP_Text)obj; if ((Object)(object)val != (Object)null) { Color valueOrDefault = _aliasColor.GetValueOrDefault(); if (!_aliasColor.HasValue) { valueOrDefault = ((Graphic)val).color; _aliasColor = valueOrDefault; } ((Graphic)val).color = (Color)(flag ? new Color(0.25f, 0.95f, 0.3f) : _aliasColor.Value); } SetText((characterLoreDisplay != null) ? characterLoreDisplay.descriptionLocalizer : null, Strings.Get((kind == Kind.Instant) ? "w_instant" : "w_surprise")); CharacterRankDisplay characterRankDisplay = panel.characterRankDisplay; if (characterRankDisplay != null) { ((Component)characterRankDisplay).gameObject.SetActive(false); } LockedCharacterInfoDisplay lockedCharacterInfoDisplay = panel.lockedCharacterInfoDisplay; if (lockedCharacterInfoDisplay != null) { ((Component)lockedCharacterInfoDisplay).gameObject.SetActive(false); } LocalCharacterDisplay localCharacterDisplay = panel.localCharacterDisplay; RawImage val2 = ((localCharacterDisplay != null) ? localCharacterDisplay.image : null); if ((Object)(object)val2 != (Object)null) { ((Graphic)val2).color = Color.black; } if (!_cycling && !_rolling) { MelonCoroutines.Start(CycleSilhouettes(panel)); } } private static void Restore(CharacterPanel panel, bool isLocked) { //IL_00b6: Unknown result type (might be due to invalid IL or missing references) CharacterLoreDisplay characterLoreDisplay = panel.characterLoreDisplay; LocalizeStringEvent[] array = new LocalizeStringEvent[3]; CharacterInfoDisplay characterInfoDisplay = panel.characterInfoDisplay; object obj; if (characterInfoDisplay == null) { obj = null; } else { CharacterUIVisualizer characterVisualizer = characterInfoDisplay.characterVisualizer; obj = ((characterVisualizer != null) ? characterVisualizer.nameLocalizer : null); } array[0] = (LocalizeStringEvent)obj; array[1] = ((characterLoreDisplay != null) ? characterLoreDisplay.aliasLocalizer : null); array[2] = ((characterLoreDisplay != null) ? characterLoreDisplay.descriptionLocalizer : null); LocalizeStringEvent[] array2 = (LocalizeStringEvent[])(object)array; foreach (LocalizeStringEvent val in array2) { if ((Object)(object)val != (Object)null && !((Behaviour)val).enabled) { ((Behaviour)val).enabled = true; val.RefreshString(); } } object obj2; if (characterLoreDisplay == null) { obj2 = null; } else { LocalizeStringEvent aliasLocalizer = characterLoreDisplay.aliasLocalizer; obj2 = ((aliasLocalizer != null) ? ((Component)aliasLocalizer).GetComponent<TMP_Text>() : null); } TMP_Text val2 = (TMP_Text)obj2; if ((Object)(object)val2 != (Object)null && _aliasColor.HasValue) { ((Graphic)val2).color = _aliasColor.Value; } LoreAndDetailsDisplayToggler loreAndDetailsDisplayToggler = panel.loreAndDetailsDisplayToggler; if (loreAndDetailsDisplayToggler != null) { ButtonPrompt toggleLoreDisplayPrompt = loreAndDetailsDisplayToggler.toggleLoreDisplayPrompt; if (toggleLoreDisplayPrompt != null) { ((Component)toggleLoreDisplayPrompt).gameObject.SetActive(true); } } LocalCharacterDisplay localCharacterDisplay = panel.localCharacterDisplay; if (localCharacterDisplay != null) { localCharacterDisplay.SetDarkened(isLocked); } } private static void SetText(LocalizeStringEvent loc, string text) { if (!((Object)(object)loc == (Object)null)) { ((Behaviour)loc).enabled = false; TMP_Text val = ((Component)loc).GetComponent<TMP_Text>() ?? ((Component)loc).GetComponentInChildren<TMP_Text>(true); if ((Object)(object)val != (Object)null) { val.text = text; } } } private static SkinId NextSkin() { //IL_0045: Unknown result type (might be due to invalid IL or missing references) int num = Rnd.Next(Skins.Count); if (num == _lastSkin && Skins.Count > 1) { num = (num + 1) % Skins.Count; } _lastSkin = num; return Skins[num]; } private static void ShowSilhouette(CharacterPanel panel, SkinId skin) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) LocalCharacterDisplay localCharacterDisplay = panel.localCharacterDisplay; localCharacterDisplay.Display(skin); if ((Object)(object)localCharacterDisplay.image != (Object)null) { ((Graphic)localCharacterDisplay.image).color = Color.black; } } private static IEnumerator CycleSilhouettes(CharacterPanel panel) { _cycling = true; while (_shown.HasValue && !_rolling && (Object)(object)panel != (Object)null && ((Behaviour)panel).isActiveAndEnabled && Skins.Count > 0) { try { ShowSilhouette(panel, NextSkin()); } catch (Exception ex) { RunCustomizerMod.Log.Error("[random wizard] silhouettes: " + ex.Message); break; } yield return (object)new WaitForSeconds(0.35f); } _cycling = false; } private static IEnumerator Roulette(CharacterSelector selector, CharacterPanel panel, CharacterButton target) { _rolling = true; Show(panel, Kind.Instant); float delay = 0.05f; while (delay < 0.3f && (Object)(object)panel != (Object)null && Skins.Count > 0) { try { ShowSilhouette(panel, NextSkin()); } catch (Exception ex) { RunCustomizerMod.Log.Error("[random wizard] roulette: " + ex.Message); break; } yield return (object)new WaitForSeconds(delay); delay *= 1.18f; } _rolling = false; _shown = null; try { selector.OnCharacterButtonSelected(target); } catch (Exception value) { RunCustomizerMod.Log.Error($"[random wizard] {value}"); } } private static IEnumerator CycleSeatModel() { _seatCycling = true; while (_pendingSurprise && Skins.Count > 0) { try { PlayerSeatUpdater instance = Singleton<PlayerSeatUpdater>.Instance; PlayerSeat obj = ((instance != null) ? instance.GetLocalPlayerSeat() : null); if (obj != null) { CharacterModelVisualizer characterModelVisualizer = obj.characterModelVisualizer; if (characterModelVisualizer != null) { characterModelVisualizer.Show(NextSkin(), false, false); } } } catch { } yield return (object)new WaitForSeconds(0.6f); } _seatCycling = false; try { PlayerSeatUpdater instance2 = Singleton<PlayerSeatUpdater>.Instance; if (instance2 != null) { instance2.RefreshSeats(); } } catch { } } internal static void AllowClicks(Transform from, Transform upTo) { Transform parent = from.parent; while ((Object)(object)parent != (Object)null && (Object)(object)parent != (Object)(object)upTo) { CanvasGroup component = ((Component)parent).GetComponent<CanvasGroup>(); if (!((Object)(object)component == (Object)null)) { component.blocksRaycasts = true; component.interactable = true; } parent = parent.parent; } } public static void StartLevelPrefix() { if (Modes.IsHost) { ApplySurprise(); } } public static void PrepareForLevelStartPrefix() { if (!Modes.IsHost) { ApplySurprise(); } } private static void ApplySurprise() { //IL_00cc: Unknown result type (might be due to invalid IL or missing references) if (!_pendingSurprise || _appliedFrame == Time.frameCount) { return; } _appliedFrame = Time.frameCount; try { CharacterSelector val = null; foreach (CharacterSelector item in Resources.FindObjectsOfTypeAll<CharacterSelector>()) { if ((Object)(object)item != (Object)null) { List<CharacterButton> initializedCharacterButtons = item.initializedCharacterButtons; if (initializedCharacterButtons != null && initializedCharacterButtons.Count > 0) { val = item; break; } } } CharacterButto