Decompiled source of PeglinRelicRerolls v1.0.1
PeglinRelicRerolls.dll
Decompiled 5 days 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.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using Audio; using Battle; using Battle.Attacks; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using Cruciball; using DG.Tweening; using DG.Tweening.Core; using DG.Tweening.Plugins.Options; using HarmonyLib; using I2.Loc; using Map; using Microsoft.CodeAnalysis; using Peglin.ClassSystem; using PeglinRelicRerolls.Core; using PeglinUI; using PeglinUI.MainMenu.Cruciball; using PeglinUI.OrbDisplay; using PeglinUI.PostBattle; using Relics; using Saving; using Scenarios.Shop; using TMPro; using UI; using UnityEngine; using UnityEngine.Audio; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.SceneManagement; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("PeglinRelicRerolls")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.1.0")] [assembly: AssemblyInformationalVersion("1.0.1+d9fbc65b988e1d0cf8a484b791c1f254d6993d62")] [assembly: AssemblyProduct("PeglinRelicRerolls")] [assembly: AssemblyTitle("PeglinRelicRerolls")] [assembly: AssemblyVersion("1.0.1.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace PeglinRelicRerolls { internal static class BombCelebration { private const int BombCount = 14; private const float SpreadSeconds = 1.2f; private const float BangSeconds = 0.3f; private const float ExplosionSeconds = 0.65f; private const float BangVolume = 0.6f; private static GameObject _bombPrefab; private static AudioClip _bangSound; private static AudioMixerGroup _sfxMixer; private static AudioSource _audio; private static readonly Random Random = new Random(); public static void Remember(GameObject prefab, PegAudioManager audioManager) { if ((Object)(object)prefab != (Object)null) { _bombPrefab = prefab; } if ((Object)(object)audioManager != (Object)null) { Traverse obj = Traverse.Create((object)audioManager); AudioClip value = obj.Field("_bombExplosionSfx").GetValue<AudioClip>(); if ((Object)(object)value != (Object)null) { _bangSound = value; } AudioSource value2 = obj.Field("_sfxAudioSource").GetValue<AudioSource>(); if ((Object)(object)value2 != (Object)null && (Object)(object)value2.outputAudioMixerGroup != (Object)null) { _sfxMixer = value2.outputAudioMixerGroup; } } } public static void Play() { GameObject val = FindPrefab(); Camera main = Camera.main; if ((Object)(object)val == (Object)null || (Object)(object)main == (Object)null) { Plugin.Log.LogWarning((object)"Bomb celebration skipped: bomb prefab not loaded yet (fight a battle first)."); return; } RerollHud.Ensure(); RerollHud.RunCoroutine(Celebrate(val, main)); } private static GameObject FindPrefab() { if ((Object)(object)_bombPrefab != (Object)null) { return _bombPrefab; } BombLob val = ((IEnumerable<BombLob>)Resources.FindObjectsOfTypeAll<BombLob>()).FirstOrDefault((Func<BombLob, bool>)delegate(BombLob bomb) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) if (!bomb.isRigged) { Scene scene = ((Component)bomb).gameObject.scene; return !((Scene)(ref scene)).IsValid(); } return false; }); if (!((Object)(object)val != (Object)null)) { return null; } return ((Component)val).gameObject; } private static IEnumerator Celebrate(GameObject prefab, Camera camera) { int topLayer = ((SortingLayer.layers.Length != 0) ? ((SortingLayer)(ref SortingLayer.layers[SortingLayer.layers.Length - 1])).id : 0); float[] array = (from _ in Enumerable.Range(0, 14) select (float)Random.NextDouble() * 1.2f into result orderby result select result).ToArray(); float num = 0f; float[] array2 = array; foreach (float delay in array2) { if (delay > num) { yield return (object)new WaitForSecondsRealtime(delay - num); num = delay; } if ((Object)(object)camera == (Object)null) { break; } Vector3 position = camera.ViewportToWorldPoint(new Vector3(0.05f + (float)Random.NextDouble() * 0.9f, 0.08f + (float)Random.NextDouble() * 0.84f, 0f)); position.z = 0f; SpawnExplosion(prefab, position, topLayer); } } private static void SpawnExplosion(GameObject prefab, Vector3 position, int sortingLayer) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) GameObject val = Object.Instantiate<GameObject>(prefab, position, Quaternion.Euler(0f, 0f, (float)Random.NextDouble() * 360f)); BombLob component = val.GetComponent<BombLob>(); if ((Object)(object)component != (Object)null) { component.OnBombTossComplete = null; ((Behaviour)component).enabled = false; } Rigidbody2D component2 = val.GetComponent<Rigidbody2D>(); if ((Object)(object)component2 != (Object)null) { component2.isKinematic = true; component2.velocity = Vector2.zero; component2.simulated = false; } Collider2D[] componentsInChildren = val.GetComponentsInChildren<Collider2D>(); for (int i = 0; i < componentsInChildren.Length; i++) { ((Behaviour)componentsInChildren[i]).enabled = false; } Renderer[] componentsInChildren2 = val.GetComponentsInChildren<Renderer>(); foreach (Renderer obj in componentsInChildren2) { obj.sortingLayerID = sortingLayer; obj.sortingOrder = 1000; } Animator component3 = val.GetComponent<Animator>(); if ((Object)(object)component3 != (Object)null) { component3.updateMode = (AnimatorUpdateMode)2; component3.SetTrigger("explode"); } RerollHud.RunCoroutine(BangAfter(0.3f)); RerollHud.RunCoroutine(DestroyAfter(val, 0.65f)); } private static IEnumerator BangAfter(float seconds) { yield return (object)new WaitForSecondsRealtime(seconds); AudioClip val = FindBangSound(); AudioSource val2 = AudioPlayer(); if ((Object)(object)val != (Object)null && (Object)(object)val2 != (Object)null) { val2.PlayOneShot(val, 0.6f); } } private static AudioClip FindBangSound() { if ((Object)(object)_bangSound != (Object)null) { return _bangSound; } PegSfx val = ((IEnumerable<PegSfx>)Resources.FindObjectsOfTypeAll<PegSfx>()).FirstOrDefault((Func<PegSfx, bool>)((PegSfx sfx) => (Object)(object)sfx.BombExplosionSfx != (Object)null)); if (!((Object)(object)val != (Object)null)) { return null; } return val.BombExplosionSfx; } private static AudioSource AudioPlayer() { //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_001d: Expected O, but got Unknown if ((Object)(object)_audio == (Object)null) { GameObject val = new GameObject("PeglinRelicRerollsAudio"); Object.DontDestroyOnLoad((Object)val); _audio = val.AddComponent<AudioSource>(); _audio.playOnAwake = false; _audio.spatialBlend = 0f; _audio.ignoreListenerPause = true; } if ((Object)(object)_sfxMixer != (Object)null && (Object)(object)_audio.outputAudioMixerGroup != (Object)(object)_sfxMixer) { _audio.outputAudioMixerGroup = _sfxMixer; } return _audio; } private static IEnumerator DestroyAfter(GameObject target, float seconds) { yield return (object)new WaitForSecondsRealtime(seconds); if ((Object)(object)target != (Object)null) { Object.Destroy((Object)(object)target); } } } [HarmonyPatch(typeof(BattleController), "Start")] internal static class RememberBombPrefabPatch { private static void Postfix(BattleController __instance, GameObject ____tossableBombPrefab) { BombCelebration.Remember(____tossableBombPrefab, ((Component)__instance).GetComponent<PegAudioManager>()); } } [HarmonyPatch(typeof(CruciballLevelSelector), "UpdateCruciballSelection")] internal static class CruciballDescriptionPatch { private static void Postfix(CruciballLevelSelector __instance, Class ____selectedClass) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) int selectedCruciballLevel = PersistentPlayerData.Instance.GetSelectedCruciballLevel(____selectedClass); int value = Plugin.CruciballPenaltyPerLevel.Value; int[] penaltyLevels = RerollRules.ParsePenaltyLevels(Plugin.CruciballPenaltyLevels.Value); if (RerollRules.IsPenaltyLevel(value, penaltyLevels, selectedCruciballLevel) && !((Object)(object)__instance.currentLevelDescText == (Object)null)) { string arg = ((value == 1) ? "relic reroll" : "relic rerolls"); TMP_Text currentLevelDescText = __instance.currentLevelDescText; currentLevelDescText.text += $" Start with {value} fewer {arg}."; } } } internal static class DieSprite { private struct Face { public Vector2 Origin; public Vector2 EdgeU; public Vector2 EdgeV; public Color Color; public Vector2[] Pips; public float PipRadius; public Vector2 ToFace(Vector2 point) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: 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_003d: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) Vector2 val = point - Origin; float num = EdgeU.x * EdgeV.y - EdgeV.x * EdgeU.y; return new Vector2((val.x * EdgeV.y - EdgeV.x * val.y) / num, (EdgeU.x * val.y - val.x * EdgeU.y) / num); } } private const int Size = 32; private const float CentreX = 16f; private const float TopY = 1.5f; private const float HalfWidth = 14f; private const float SideHeight = 15f; private static readonly Color TopColor = Color32.op_Implicit(new Color32((byte)234, (byte)92, (byte)112, byte.MaxValue)); private static readonly Color LeftColor = Color32.op_Implicit(new Color32((byte)170, (byte)30, (byte)52, byte.MaxValue)); private static readonly Color RightColor = Color32.op_Implicit(new Color32((byte)214, (byte)50, (byte)72, byte.MaxValue)); private static readonly Color OutlineColor = Color32.op_Implicit(new Color32((byte)64, (byte)14, (byte)24, byte.MaxValue)); private static readonly Color PipColor = Color32.op_Implicit(new Color32(byte.MaxValue, (byte)250, (byte)244, byte.MaxValue)); private static Sprite _sprite; public static Sprite Get() { //IL_007a: 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_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0083: 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_0089: 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_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //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_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: 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_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: 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_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_0173: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_018f: 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) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_01be: Unknown result type (might be due to invalid IL or missing references) //IL_01c3: Unknown result type (might be due to invalid IL or missing references) //IL_01d4: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: Unknown result type (might be due to invalid IL or missing references) //IL_036c: Unknown result type (might be due to invalid IL or missing references) //IL_0371: Unknown result type (might be due to invalid IL or missing references) //IL_0378: Unknown result type (might be due to invalid IL or missing references) //IL_0381: Expected O, but got Unknown //IL_023f: Unknown result type (might be due to invalid IL or missing references) //IL_0241: Unknown result type (might be due to invalid IL or missing references) //IL_03fe: Unknown result type (might be due to invalid IL or missing references) //IL_040d: Unknown result type (might be due to invalid IL or missing references) //IL_0278: Unknown result type (might be due to invalid IL or missing references) //IL_027a: Unknown result type (might be due to invalid IL or missing references) //IL_027f: Unknown result type (might be due to invalid IL or missing references) //IL_0281: Unknown result type (might be due to invalid IL or missing references) //IL_0391: Unknown result type (might be due to invalid IL or missing references) //IL_0396: Unknown result type (might be due to invalid IL or missing references) //IL_0292: Unknown result type (might be due to invalid IL or missing references) //IL_03c2: Unknown result type (might be due to invalid IL or missing references) //IL_02a3: Unknown result type (might be due to invalid IL or missing references) //IL_03b2: Unknown result type (might be due to invalid IL or missing references) //IL_03b7: Unknown result type (might be due to invalid IL or missing references) //IL_02b4: Unknown result type (might be due to invalid IL or missing references) //IL_02c4: Unknown result type (might be due to invalid IL or missing references) //IL_02c9: Unknown result type (might be due to invalid IL or missing references) //IL_02dd: Unknown result type (might be due to invalid IL or missing references) //IL_02e2: Unknown result type (might be due to invalid IL or missing references) //IL_02e4: Unknown result type (might be due to invalid IL or missing references) //IL_02e6: Unknown result type (might be due to invalid IL or missing references) //IL_02e8: Unknown result type (might be due to invalid IL or missing references) //IL_02ed: Unknown result type (might be due to invalid IL or missing references) //IL_0322: Unknown result type (might be due to invalid IL or missing references) //IL_0324: Unknown result type (might be due to invalid IL or missing references) //IL_0307: Unknown result type (might be due to invalid IL or missing references) //IL_030c: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_sprite != (Object)null) { return _sprite; } Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(16f, 1.5f); Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(2f, 8.5f); Vector2 val3 = default(Vector2); ((Vector2)(ref val3))..ctor(30f, 8.5f); Vector2 val4 = default(Vector2); ((Vector2)(ref val4))..ctor(16f, 15.5f); Vector2 edgeV = default(Vector2); ((Vector2)(ref edgeV))..ctor(0f, 15f); Face[] array = new Face[3] { new Face { Origin = val, EdgeU = val3 - val, EdgeV = val2 - val, Color = TopColor, Pips = (Vector2[])(object)new Vector2[1] { new Vector2(0.5f, 0.5f) }, PipRadius = 0.17f }, new Face { Origin = val2, EdgeU = val4 - val2, EdgeV = edgeV, Color = LeftColor, Pips = (Vector2[])(object)new Vector2[2] { new Vector2(0.28f, 0.28f), new Vector2(0.72f, 0.72f) }, PipRadius = 0.13f }, new Face { Origin = val4, EdgeU = val3 - val4, EdgeV = edgeV, Color = RightColor, Pips = (Vector2[])(object)new Vector2[3] { new Vector2(0.75f, 0.22f), new Vector2(0.5f, 0.5f), new Vector2(0.25f, 0.78f) }, PipRadius = 0.12f } }; Color val5 = default(Color); ((Color)(ref val5))..ctor(0f, 0f, 0f, 0f); Color[,] array2 = new Color[32, 32]; bool[,] array3 = new bool[32, 32]; Vector2 point = default(Vector2); for (int i = 0; i < 32; i++) { for (int j = 0; j < 32; j++) { array2[j, i] = val5; ((Vector2)(ref point))..ctor((float)j + 0.5f, (float)i + 0.5f); Face[] array4 = array; for (int k = 0; k < array4.Length; k++) { Face face = array4[k]; Vector2 val6 = face.ToFace(point); if (val6.x < 0f || val6.x > 1f || val6.y < 0f || val6.y > 1f) { continue; } Color val7 = face.Color; Vector2[] pips = face.Pips; foreach (Vector2 val8 in pips) { Vector2 val9 = val6 - val8; if (((Vector2)(ref val9)).sqrMagnitude <= face.PipRadius * face.PipRadius) { val7 = PipColor; } } array2[j, i] = val7; array3[j, i] = true; break; } } } Texture2D val10 = new Texture2D(32, 32, (TextureFormat)4, false) { filterMode = (FilterMode)0, wrapMode = (TextureWrapMode)1 }; for (int m = 0; m < 32; m++) { for (int n = 0; n < 32; n++) { Color val11 = array2[n, m]; if (array3[n, m] && IsEdge(array3, n, m)) { val11 = OutlineColor; } val10.SetPixel(n, 31 - m, val11); } } val10.Apply(); _sprite = Sprite.Create(val10, new Rect(0f, 0f, 32f, 32f), new Vector2(0.5f, 0.5f), 32f); ((Object)_sprite).name = "PeglinRelicRerollsDie"; return _sprite; } private static bool IsEdge(bool[,] filled, int x, int y) { if (x != 0 && y != 0 && x != 31 && y != 31 && filled[x - 1, y] && filled[x + 1, y] && filled[x, y - 1]) { return !filled[x, y + 1]; } return true; } } internal static class ModLog { public static void Info(string message) { if (Plugin.EnableLogging == null || Plugin.EnableLogging.Value) { Plugin.Log.LogInfo((object)message); } } } [BepInPlugin("PeglinRelicRerolls", "PeglinRelicRerolls", "1.0.1")] public class Plugin : BaseUnityPlugin { public const string Guid = "PeglinRelicRerolls"; public const string Name = "PeglinRelicRerolls"; public const string Version = "1.0.1"; public const string SupportedGameVersion = "2.0.12"; private const string LegacyConfigFile = "com.pierremarais.peglin.rerolls.cfg"; internal const string LegacyStateFile = "PeglinRerolls.state"; internal static ManualLogSource Log; internal static ConfigEntry<int> StartingRerolls; internal static ConfigEntry<bool> EnableBattleReroll; internal static ConfigEntry<bool> EnableChestReroll; internal static ConfigEntry<bool> EnableShopReroll; internal static ConfigEntry<bool> EnableShopRelicReroll; internal static ConfigEntry<int> ShopRerollLimit; internal static ConfigEntry<int> ShopRerollCost; internal static ConfigEntry<double> ChestRareUpgradeChance; internal static ConfigEntry<double> ChestBossUpgradeChance; internal static ConfigEntry<KeyboardShortcut> RerollHotkey; internal static ConfigEntry<KeyboardShortcut> ControllerRerollButton; internal static ConfigEntry<bool> ShowRerollBadge; internal static ConfigEntry<bool> EnableLogging; internal static ConfigEntry<int> BossRerollLimit; internal static ConfigEntry<string> CruciballPenaltyLevels; internal static ConfigEntry<int> CruciballPenaltyPerLevel; private const string KeyHelp = " Key names: https://docs.unity3d.com/ScriptReference/KeyCode.html"; internal static bool DevModeActive => false; private void CheckGameVersion() { string version = Application.version; if (!(version == "2.0.12")) { Log.LogWarning((object)("PeglinRelicRerolls 1.0.1 was built for Peglin v2.0.12, but this is Peglin v" + version + ". Rerolls may not work correctly until the mod is updated for this game version.")); } } private void MigrateLegacyConfig() { string text = Path.Combine(Paths.ConfigPath, "com.pierremarais.peglin.rerolls.cfg"); if (!File.Exists(text) || File.Exists(((BaseUnityPlugin)this).Config.ConfigFilePath)) { return; } try { File.Move(text, ((BaseUnityPlugin)this).Config.ConfigFilePath); ((BaseUnityPlugin)this).Config.Reload(); Log.LogInfo((object)("Moved settings from com.pierremarais.peglin.rerolls.cfg to " + Path.GetFileName(((BaseUnityPlugin)this).Config.ConfigFilePath) + ".")); } catch (Exception ex) { Log.LogWarning((object)("Couldn't move com.pierremarais.peglin.rerolls.cfg to the new config name: " + ex.Message)); } } private void Awake() { //IL_004e: 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_014a: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Expected O, but got Unknown //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Expected O, but got Unknown //IL_028c: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; MigrateLegacyConfig(); CheckGameVersion(); StartingRerolls = ((BaseUnityPlugin)this).Config.Bind<int>("General", "StartingRerolls", 5, "Rerolls granted at the start of every new run."); RerollHotkey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("General", "RerollHotkey", new KeyboardShortcut((KeyCode)116, Array.Empty<KeyCode>()), "Keyboard shortcut for the reroll button on relic screens and in the shop. Key names: https://docs.unity3d.com/ScriptReference/KeyCode.html"); ControllerRerollButton = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("General", "ControllerRerollButton", new KeyboardShortcut((KeyCode)339, Array.Empty<KeyCode>()), "Controller button for rerolling. Default JoystickButton9 is clicking the right stick (R3) on Xbox-layout controllers, including PlayStation and other pads when Steam Input is on. Set to None to disable. Key names: https://docs.unity3d.com/ScriptReference/KeyCode.html"); ShowRerollBadge = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ShowRerollBadge", true, "Show a die with your reroll count above the relic bar, whenever the relic bar is on screen."); EnableLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableLogging", true, "Write reroll details (offers, do-not-offer lists, saves) to BepInEx/LogOutput.log. Warnings and errors are always logged."); EnableBattleReroll = ((BaseUnityPlugin)this).Config.Bind<bool>("Relic Screens", "EnableBattleReroll", true, "Show the reroll button on the relic reward screen after miniboss/boss battles. Rerolls keep the same rarity."); EnableChestReroll = ((BaseUnityPlugin)this).Config.Bind<bool>("Relic Screens", "EnableChestReroll", true, "Show the reroll button when opening a treasure chest."); ChestRareUpgradeChance = ((BaseUnityPlugin)this).Config.Bind<double>("Relic Screens", "ChestRareUpgradeChance", 0.15, new ConfigDescription("Chance that rerolling a common chest offers a rare relic instead.", (AcceptableValueBase)(object)new AcceptableValueRange<double>(0.0, 1.0), Array.Empty<object>())); ChestBossUpgradeChance = ((BaseUnityPlugin)this).Config.Bind<double>("Relic Screens", "ChestBossUpgradeChance", 0.001, new ConfigDescription("Chance of something special when rerolling a rare chest.", (AcceptableValueBase)(object)new AcceptableValueRange<double>(0.0, 1.0), Array.Empty<object>())); BossRerollLimit = ((BaseUnityPlugin)this).Config.Bind<int>("Relic Screens", "BossRerollLimit", 1, "Rerolls allowed on the floor boss relic reward. Set to -1 for no limit."); EnableShopReroll = ((BaseUnityPlugin)this).Config.Bind<bool>("Shop", "EnableShopReroll", true, "Replace one common relic in each shop with a purchasable reroll (one per shop)."); EnableShopRelicReroll = ((BaseUnityPlugin)this).Config.Bind<bool>("Shop", "EnableShopRelicReroll", true, "Allow spending a reroll in the shop to reroll its unbought relics."); ShopRerollLimit = ((BaseUnityPlugin)this).Config.Bind<int>("Shop", "ShopRerollLimit", 3, "How many times each shop's relics can be rerolled. Set to -1 for no limit."); ShopRerollCost = ((BaseUnityPlugin)this).Config.Bind<int>("Shop", "ShopRerollCost", 20, "Gold cost of a reroll in the shop. Doubled by Wand of Greed and in act 4, like relic prices."); CruciballPenaltyLevels = ((BaseUnityPlugin)this).Config.Bind<string>("Cruciball", "PenaltyLevels", "5,10,15", "Comma-separated cruciball levels that each reduce your starting rerolls."); CruciballPenaltyPerLevel = ((BaseUnityPlugin)this).Config.Bind<int>("Cruciball", "PenaltyPerLevel", 1, "Rerolls removed for each penalty level reached. Set to 0 to disable."); new Harmony("PeglinRelicRerolls").PatchAll(typeof(Plugin).Assembly); Log.LogInfo((object)"PeglinRelicRerolls 1.0.1 loaded."); } } internal static class RelicPool { private static readonly MethodInfo PossibleRelicsOfRarity = AccessTools.Method(typeof(RelicManager), "GetAllPosibleRelicsOfRarityList", (Type[])null, (Type[])null); private static readonly MethodInfo IsRelicValid = AccessTools.Method(typeof(RelicManager), "CheckIfRelicValid", (Type[])null, (Type[])null); public static int CountAvailable(RelicManager relicManager, RelicRarity rarity, IEnumerable<Relic> exclude) { //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)relicManager == (Object)null || PossibleRelicsOfRarity == null || IsRelicValid == null) { return int.MaxValue; } try { IEnumerable obj = (IEnumerable)PossibleRelicsOfRarity.Invoke(relicManager, new object[2] { rarity, false }); List<Relic> list = Exclusions(relicManager, exclude); int num = 0; foreach (object item in obj) { if ((bool)IsRelicValid.Invoke(relicManager, new object[4] { item, rarity, list, false })) { num++; } } return num; } catch (Exception ex) { Plugin.Log.LogWarning((object)$"Couldn't count {rarity} relics: {ex.Message}"); return int.MaxValue; } } public static Relic[] Draw(RelicManager relicManager, RelicRarity rarity, int count, IEnumerable<Relic> exclude) { //IL_0014: 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) Relic[] multipleRelicsOffOfQueue = relicManager.GetMultipleRelicsOffOfQueue(count, rarity, false); RelicRarity[] array = Enumerable.Repeat<RelicRarity>(rarity, count).ToArray(); return (from relic in relicManager.CheckAndUpdateSeededRelicOffers(multipleRelicsOffOfQueue, array, Exclusions(relicManager, exclude).ToArray(), false, false) where (Object)(object)relic != (Object)null && (Object)(object)relic != (Object)(object)relicManager.consolationPrize select relic).Distinct().ToArray(); } public static List<Relic> AvailableShopOnly(RelicManager relicManager, IEnumerable<Relic> exclude) { HashSet<Relic> excluded = new HashSet<Relic>(exclude.Where((Relic relic) => (Object)(object)relic != (Object)null)); return (relicManager.ShopRelicPool ?? new List<Relic>()).Where((Relic relic) => (Object)(object)relic != (Object)null && !excluded.Contains(relic) && !Owns(relicManager, relic)).Distinct().ToList(); } public static Relic DrawShopOnly(RelicManager relicManager, IEnumerable<Relic> exclude) { HashSet<Relic> hashSet = new HashSet<Relic>(exclude.Where((Relic relic) => (Object)(object)relic != (Object)null)); int num = relicManager.ShopRelicPool?.Count ?? 0; for (int num2 = 0; num2 < num; num2++) { Relic shopOnlyRelicOffOfQueue = relicManager.GetShopOnlyRelicOffOfQueue(); if ((Object)(object)shopOnlyRelicOffOfQueue != (Object)null && !hashSet.Contains(shopOnlyRelicOffOfQueue)) { return shopOnlyRelicOffOfQueue; } } return null; } public static RelicRarity RarityOf(RelicManager relicManager, Relic relic) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_011e: 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_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) object value = Traverse.Create((object)relicManager).Field("_selectedClass").GetValue(); RelicRarity[] array = new RelicRarity[3]; RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/); RelicRarity[] array2 = (RelicRarity[])(object)array; foreach (RelicRarity val in array2) { if (PossibleRelicsOfRarity == null || !(PossibleRelicsOfRarity.Invoke(relicManager, new object[2] { val, false }) is IEnumerable enumerable)) { continue; } foreach (object item in enumerable) { Traverse val2 = Traverse.Create(item); if (!((Object)(object)val2.Field("relic").GetValue<Relic>() != (Object)(object)relic)) { if (val2.Field("classRarities").GetValue() is IDictionary dictionary && value != null && dictionary.Contains(value)) { return (RelicRarity)dictionary[value]; } return val; } } } if (!((Object)(object)relic != (Object)null)) { return (RelicRarity)1; } return relic.globalRarity; } public static List<Relic> Exclusions(RelicManager relicManager, IEnumerable<Relic> relics) { List<Relic> excluded = relics.Where((Relic relic) => (Object)(object)relic != (Object)null).ToList(); excluded.AddRange(from relic in ShopRelics(relicManager) where !excluded.Contains(relic) select relic); return excluded; } public static IEnumerable<Relic> ShopRelics(RelicManager relicManager) { HashSet<Relic> hashSet = new HashSet<Relic>(); if (relicManager.ShopRelicPool != null) { hashSet.UnionWith(relicManager.ShopRelicPool.Where((Relic relic) => (Object)(object)relic != (Object)null)); } if (Traverse.Create((object)relicManager).Field("_classLoadouts").GetValue() is IEnumerable enumerable) { foreach (object item in enumerable) { List<Relic> value = Traverse.Create(item).Field("Loadout").Field("ShopRelicOverrides") .Field("relics") .GetValue<List<Relic>>(); if (value != null) { hashSet.UnionWith(value.Where((Relic relic) => (Object)(object)relic != (Object)null)); } } } return hashSet; } public static string DisplayName(Relic relic) { if (!((Object)(object)relic == (Object)null)) { if (!string.IsNullOrEmpty(relic.englishDisplayName)) { return relic.englishDisplayName; } return ((Object)relic).name; } return "(none)"; } private static bool Owns(RelicManager relicManager, Relic relic) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) if (Traverse.Create((object)relicManager).Field("_ownedRelics").GetValue() is IDictionary dictionary) { return dictionary.Contains(relic.effect); } return false; } } internal static class RelicRerollSession { private static readonly RelicScreenInstance<Relic> Instance = new RelicScreenInstance<Relic>(); private static BattleUpgradeCanvas _canvas; private static RelicRarity _baseRarity; private static bool _isTreasure; private static RelicIcon[] _slots = (RelicIcon[])(object)new RelicIcon[0]; private static int _unseenInScreenPool; private static int _notOnScreenInPool; private static bool IsBossReward { get { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Invalid comparison between Unknown and I4 if (!_isTreasure) { return (int)_baseRarity == 3; } return false; } } private static int AvailableInScreenPool => RerollRules.AvailableToReroll(_unseenInScreenPool, _notOnScreenInPool); public static int RemainingOnScreen => RerollRules.RemainingOnScreen(IsBossReward, Instance.RerollsUsed, Plugin.BossRerollLimit.Value); public static bool CanRerollNow => RerollRules.CanReroll(RerollState.Count, RemainingOnScreen, AvailableInScreenPool); public static bool ScreenLimitReached => RemainingOnScreen <= 0; public static bool PoolExhausted => AvailableInScreenPool <= 0; public static string DisabledReason { get { if (ScreenLimitReached) { if (Plugin.BossRerollLimit.Value != 0) { return "disabled, the floor boss reroll has been used"; } return "disabled for floor boss rewards"; } if (PoolExhausted) { if (!IsBossReward) { return "disabled for this non floor boss encounter, no more relics to reroll into"; } return "disabled, no more boss relics to reroll into"; } return null; } } public static bool IsActive { get { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Invalid comparison between Unknown and I4 if ((Object)(object)_canvas == (Object)null || !((Behaviour)_canvas).isActiveAndEnabled) { return false; } if (GameBlockingWindow.windowOpen) { return false; } Traverse val = Traverse.Create((object)_canvas); if ((int)val.Field("_state").GetValue<State>() != 1) { return false; } CanvasGroup value = val.Field("_mainCanvasGroup").GetValue<CanvasGroup>(); if ((Object)(object)value != (Object)null && !value.interactable) { return false; } GameObject value2 = val.Field("_relicPanel").GetValue<GameObject>(); if ((Object)(object)value2 == (Object)null || !value2.activeInHierarchy) { return false; } UpgradeConfirmationRelic value3 = val.Field("_upgradeConfirmationRelic").GetValue<UpgradeConfirmationRelic>(); if (!((Object)(object)value3 == (Object)null)) { return !((Component)value3).gameObject.activeInHierarchy; } return true; } } public static void Begin(BattleUpgradeCanvas canvas, RelicRarity rarity, bool isTreasure) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) _canvas = canvas; _baseRarity = rarity; _isTreasure = isTreasure; Instance.Enter(canvas); _slots = OfferedIcons(); Instance.MarkSeen(OfferedRelics()); RefreshPoolCounts(RelicManagerFor(canvas)); RerollHud.Ensure(); ModLog.Info($"Relic screen opened: rarity={rarity}, chest={isTreasure}"); LogSeen(); } private static void RefreshPoolCounts(RelicManager relicManager) { //IL_0001: 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) _unseenInScreenPool = RelicPool.CountAvailable(relicManager, _baseRarity, Instance.Seen); _notOnScreenInPool = RelicPool.CountAvailable(relicManager, _baseRarity, OfferedRelics()); } private static RelicManager RelicManagerFor(BattleUpgradeCanvas canvas) { if (!((Object)(object)canvas != (Object)null)) { return null; } return Traverse.Create((object)canvas).Field("_relicManager").GetValue<RelicManager>(); } public static void EndInstance() { if (Instance.Seen.Count > 0) { ModLog.Info($"Moved on: cleared {Instance.Seen.Count} relic(s) from the do-not-offer list."); } Instance.Clear(); } private static void LogSeen() { //IL_005e: Unknown result type (might be due to invalid IL or missing references) string arg = string.Join(", ", Instance.Seen.Select(RelicPool.DisplayName)); ModLog.Info($"Do-not-offer list for this screen ({Instance.Seen.Count}): {arg}"); ModLog.Info($"{_baseRarity} relics left: {_unseenInScreenPool} unseen, {_notOnScreenInPool} not on screen (shop relics excluded)"); } public static void TryReroll() { //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: 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_019a: Unknown result type (might be due to invalid IL or missing references) //IL_020d: Unknown result type (might be due to invalid IL or missing references) //IL_020f: Invalid comparison between Unknown and I4 //IL_0211: Unknown result type (might be due to invalid IL or missing references) //IL_0217: Invalid comparison between Unknown and I4 if (!IsActive || PauseMenu.Paused) { return; } RelicManager val = RelicManagerFor(_canvas); RelicIcon[] array = _slots.Where((RelicIcon icon) => (Object)(object)icon != (Object)null).ToArray(); if ((Object)(object)val == (Object)null || array.Length == 0 || !CanRerollNow || !RerollState.TrySpend()) { UIAudioManager.InvalidSelectionSfxRequested?.Invoke(); return; } if (RerollRules.ShouldRecycleSeen(_unseenInScreenPool, _notOnScreenInPool)) { Instance.RecycleSeen(OfferedRelics()); _unseenInScreenPool = _notOnScreenInPool; ModLog.Info("Do-not-offer list was full: recycled it, keeping only the relics on screen."); } RelicRarity val2 = RollRarity(val); Relic[] array2 = RelicPool.Draw(val, val2, array.Length, Instance.Seen); if (!Instance.OffersSomethingNew(array2)) { RerollState.Refund(); UIAudioManager.InvalidSelectionSfxRequested?.Invoke(); ModLog.Info("Reroll refunded: no unseen relics left to offer."); return; } HideRelicTooltip hideTooltip = RelicIcon.HideTooltip; if (hideTooltip != null) { hideTooltip.Invoke(false); } for (int num = 0; num < array.Length; num++) { bool flag = num < array2.Length; ((Component)((Component)array[num]).transform.parent).gameObject.SetActive(flag); if (flag) { array[num].SetRelic(array2[num]); array[num].shouldShowTooltip = false; RerollAnimation.FlipIn(((Component)array[num]).transform.parent, num); } } RerollAnimation.PlaySound(); KeepControllerSelectionVisible(array); Instance.MarkSeen(array2); Instance.RecordReroll(); RefreshPoolCounts(val); ModLog.Info(string.Format("Rerolled at {0}: {1} of {2} slot(s) filled: {3}. ", val2, array2.Length, array.Length, string.Join(", ", array2.Select(RelicPool.DisplayName))) + $"Rerolls left: {RerollState.Count}"); LogSeen(); if ((int)val2 == 3 && (int)_baseRarity != 3) { ModLog.Info("Chest rerolled into a boss relic!"); BombCelebration.Play(); } } private static void KeepControllerSelectionVisible(RelicIcon[] icons) { EventSystem current = EventSystem.current; GameObject val = (((Object)(object)current != (Object)null) ? current.currentSelectedGameObject : null); if (!((Object)(object)val == (Object)null) && !val.activeInHierarchy && icons.Length != 0) { current.SetSelectedGameObject(((Component)((Component)icons[0]).transform.parent).gameObject); } } private static RelicIcon[] OfferedIcons() { GameObject val = (((Object)(object)_canvas != (Object)null) ? Traverse.Create((object)_canvas).Field("_relicPanel").GetValue<GameObject>() : null); if (!((Object)(object)val != (Object)null)) { return (RelicIcon[])(object)new RelicIcon[0]; } return val.GetComponentsInChildren<RelicIcon>(); } private static Relic[] OfferedRelics() { return (from icon in OfferedIcons() select icon.relic into relic where (Object)(object)relic != (Object)null select relic).ToArray(); } private static RelicRarity RollRarity(RelicManager relicManager) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Invalid comparison between Unknown and I4 //IL_002d: 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) //IL_0015: Invalid comparison between Unknown and I4 //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) if (!_isTreasure || ((int)_baseRarity != 1 && (int)_baseRarity != 2)) { return _baseRarity; } double roll = RerollRules.UpgradeRoll(StaticGameData.currentSeed, RerollState.Used); ChestTier chestTier = ToTier(_baseRarity); ChestTier chestTier2 = RerollRules.ChestRerollTier(chestTier, roll, Plugin.ChestRareUpgradeChance.Value, Plugin.ChestBossUpgradeChance.Value); int unseenInRolledPool = ((chestTier2 == chestTier) ? _unseenInScreenPool : RelicPool.CountAvailable(relicManager, ToRarity(chestTier2), Instance.Seen)); return ToRarity(RerollRules.AvailableTier(chestTier, chestTier2, unseenInRolledPool)); } private static ChestTier ToTier(RelicRarity rarity) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Invalid comparison between Unknown and I4 if ((int)rarity != 2) { if ((int)rarity == 3) { return ChestTier.Boss; } return ChestTier.Common; } return ChestTier.Rare; } private static RelicRarity ToRarity(ChestTier tier) { return (RelicRarity)(tier switch { ChestTier.Boss => 3, ChestTier.Rare => 2, _ => 1, }); } } [HarmonyPatch(typeof(BattleUpgradeCanvas), "SetupRelicGrant")] internal static class SetupRelicGrantPatch { private static void Postfix(BattleUpgradeCanvas __instance, RelicRarity rarity, bool isTreasure) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) if (isTreasure ? Plugin.EnableChestReroll.Value : Plugin.EnableBattleReroll.Value) { RelicRerollSession.Begin(__instance, rarity, isTreasure); } } } internal static class RerollAnimation { private const float FlipSeconds = 0.22f; private const float StaggerSeconds = 0.06f; public static void FlipIn(Transform target, int order) { //IL_0013: 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_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0025: 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_0036: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)target == (Object)null)) { ShortcutExtensions.DOKill((Component)(object)target, true); Vector3 localScale = target.localScale; target.localScale = new Vector3(0f, localScale.y, localScale.z); TweenSettingsExtensions.SetUpdate<TweenerCore<Vector3, Vector3, VectorOptions>>(TweenSettingsExtensions.SetEase<TweenerCore<Vector3, Vector3, VectorOptions>>(TweenSettingsExtensions.SetDelay<TweenerCore<Vector3, Vector3, VectorOptions>>(ShortcutExtensions.DOScaleX(target, localScale.x, 0.22f), (float)order * 0.06f), (Ease)27), true); } } public static void PlaySound() { UIButtonClicked onUIButtonClicked = UIButtonClickEventDispatcher.OnUIButtonClicked; if (onUIButtonClicked != null) { onUIButtonClicked.Invoke((ButtonSfx)3); } } } internal class RerollHud : MonoBehaviour { [CompilerGenerated] private static class <>O { public static UnityAction <0>__Reroll; public static Func<KeyCode, bool> <1>__GetKeyDown; public static Func<KeyCode, bool> <2>__GetKey; } private const float ButtonHeight = 72f; private const float ButtonPadding = 48f; private const float MinButtonWidth = 380f; private const float MaxButtonWidth = 1200f; private const float IconSize = 48f; private const float IconGap = 14f; private const float CountHeightRatio = 0.44f; private static RerollHud _instance; private static double _lastRerollTime = double.NegativeInfinity; private GameObject _panel; private RectTransform _panelRect; private Image _panelImage; private Button _button; private TextMeshProUGUI _label; private Image _icon; private GameObject _badge; private RectTransform _badgeRect; private TextMeshProUGUI _badgeCount; private TextMeshProUGUI _badgeCountShadow; private float _countFontScale = 0.5f; private float _countMeasuredAt = -1f; private TMP_FontAsset _countMeasuredFont; private bool _countCalibrated; private bool _badgeStyled; private float _nextStyleAttempt; private RelicUI _relicUI; private float _nextRelicUISearch; private const string ShownState = "shown"; private string _lastBadgeState; private Transform _lastBadgeParent; private int _countCalibrationTries; private float _nextControllerCheck; private bool _controllerConnected; private readonly Vector3[] _corners = (Vector3[])(object)new Vector3[4]; public static void Ensure() { //IL_0013: 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_001e: Expected O, but got Unknown if (!((Object)(object)_instance != (Object)null)) { GameObject val = new GameObject("PeglinRelicRerollsHud"); Object.DontDestroyOnLoad((Object)val); _instance = val.AddComponent<RerollHud>(); } } public static void RunCoroutine(IEnumerator routine) { Ensure(); ((MonoBehaviour)_instance).StartCoroutine(routine); } private void Awake() { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected O, but got Unknown //IL_0079: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("Canvas", new Type[3] { typeof(Canvas), typeof(CanvasScaler), typeof(GraphicRaycaster) }); val.transform.SetParent(((Component)this).transform, false); Canvas component = val.GetComponent<Canvas>(); component.renderMode = (RenderMode)0; component.sortingOrder = 500; CanvasScaler component2 = val.GetComponent<CanvasScaler>(); component2.uiScaleMode = (ScaleMode)1; component2.referenceResolution = new Vector2(1920f, 1080f); component2.matchWidthOrHeight = 0.5f; BuildButton(val.transform); } private void BuildButton(Transform parent) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Expected O, but got Unknown //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_0097: 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_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Unknown result type (might be due to invalid IL or missing references) //IL_0188: 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) //IL_01b6: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Unknown result type (might be due to invalid IL or missing references) //IL_01e6: Unknown result type (might be due to invalid IL or missing references) //IL_01f4: Unknown result type (might be due to invalid IL or missing references) //IL_0202: Unknown result type (might be due to invalid IL or missing references) //IL_0259: Unknown result type (might be due to invalid IL or missing references) //IL_02c1: Unknown result type (might be due to invalid IL or missing references) //IL_030d: Unknown result type (might be due to invalid IL or missing references) //IL_0313: Unknown result type (might be due to invalid IL or missing references) //IL_035f: Unknown result type (might be due to invalid IL or missing references) //IL_0364: Unknown result type (might be due to invalid IL or missing references) //IL_0223: Unknown result type (might be due to invalid IL or missing references) //IL_0228: Unknown result type (might be due to invalid IL or missing references) //IL_022e: Expected O, but got Unknown _panel = new GameObject("RerollButton", new Type[4] { typeof(RectTransform), typeof(Image), typeof(Button), typeof(Outline) }); _panel.transform.SetParent(parent, false); _panelRect = _panel.GetComponent<RectTransform>(); RectTransform panelRect = _panelRect; RectTransform panelRect2 = _panelRect; RectTransform panelRect3 = _panelRect; Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(0.5f, 0f); panelRect3.pivot = val; Vector2 val2 = (panelRect.anchorMin = (panelRect2.anchorMax = val)); _panelRect.anchoredPosition = new Vector2(0f, 36f); _panelRect.sizeDelta = new Vector2(380f, 72f); _panelImage = _panel.GetComponent<Image>(); ((Graphic)_panelImage).color = Color32.op_Implicit(new Color32((byte)92, (byte)58, (byte)36, (byte)240)); Outline component = _panel.GetComponent<Outline>(); ((Shadow)component).effectColor = Color32.op_Implicit(new Color32((byte)240, (byte)196, (byte)92, byte.MaxValue)); ((Shadow)component).effectDistance = new Vector2(4f, -4f); _button = _panel.GetComponent<Button>(); ColorBlock colors = ((Selectable)_button).colors; ((ColorBlock)(ref colors)).normalColor = new Color(0.85f, 0.85f, 0.85f, 1f); ((ColorBlock)(ref colors)).highlightedColor = Color.white; ((ColorBlock)(ref colors)).pressedColor = new Color(0.6f, 0.6f, 0.6f, 1f); ((ColorBlock)(ref colors)).selectedColor = ((ColorBlock)(ref colors)).normalColor; ((ColorBlock)(ref colors)).disabledColor = new Color(0.45f, 0.45f, 0.45f, 0.75f); ((Selectable)_button).colors = colors; Button button = _button; Navigation navigation = default(Navigation); ((Navigation)(ref navigation)).mode = (Mode)0; ((Selectable)button).navigation = navigation; ButtonClickedEvent onClick = _button.onClick; object obj = <>O.<0>__Reroll; if (obj == null) { UnityAction val5 = Reroll; <>O.<0>__Reroll = val5; obj = (object)val5; } ((UnityEvent)onClick).AddListener((UnityAction)obj); _icon = new GameObject("Icon", new Type[2] { typeof(RectTransform), typeof(Image) }).GetComponent<Image>(); ((Component)_icon).transform.SetParent(_panel.transform, false); _icon.sprite = DieSprite.Get(); _icon.preserveAspect = true; ((Graphic)_icon).raycastTarget = false; ((Graphic)_icon).rectTransform.sizeDelta = new Vector2(48f, 48f); _label = CreateText("Label", _panel.transform, 34f, (TextAlignmentOptions)514); RectTransform rectTransform = ((TMP_Text)_label).rectTransform; ((Vector2)(ref val2))..ctor(0.5f, 0.5f); rectTransform.anchorMax = val2; rectTransform.anchorMin = val2; ((TMP_Text)_label).enableAutoSizing = true; ((TMP_Text)_label).fontSizeMin = 18f; ((TMP_Text)_label).fontSizeMax = 34f; ((Graphic)_label).color = Color32.op_Implicit(new Color32(byte.MaxValue, (byte)236, (byte)184, byte.MaxValue)); _panel.SetActive(false); } private void BuildBadge(Transform parent) { //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Expected O, but got Unknown //IL_00c3: 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_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_badge != (Object)null) { _badge.SetActive(false); Object.Destroy((Object)(object)_badge); } _badge = new GameObject("PeglinRelicRerollsBadge", new Type[3] { typeof(RectTransform), typeof(Image), typeof(LayoutElement) }); _badge.transform.SetParent(parent, false); _badge.transform.SetAsLastSibling(); _badge.GetComponent<LayoutElement>().ignoreLayout = true; _badgeRect = _badge.GetComponent<RectTransform>(); RectTransform badgeRect = _badgeRect; RectTransform badgeRect2 = _badgeRect; Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(0.5f, 0.5f); badgeRect2.anchorMax = val; badgeRect.anchorMin = val; _badgeRect.pivot = Vector2.zero; Image component = _badge.GetComponent<Image>(); component.sprite = DieSprite.Get(); component.preserveAspect = true; ((Graphic)component).raycastTarget = false; _badgeCountShadow = CreateBadgeText("CountShadow"); ((Graphic)_badgeCountShadow).color = Color32.op_Implicit(new Color32((byte)20, (byte)12, (byte)18, byte.MaxValue)); _badgeCount = CreateBadgeText("Count"); ((Graphic)_badgeCount).color = Color.white; _badgeStyled = false; } private TextMeshProUGUI CreateBadgeText(string name) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) TextMeshProUGUI obj = CreateText(name, _badge.transform, 36f, (TextAlignmentOptions)1028); RectTransform rectTransform = ((TMP_Text)obj).rectTransform; Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(1f, 0f); rectTransform.pivot = val; Vector2 anchorMin = (rectTransform.anchorMax = val); rectTransform.anchorMin = anchorMin; return obj; } private static TextMeshProUGUI CreateText(string name, Transform parent, float size, TextAlignmentOptions alignment) { //IL_0014: 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_0033: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name, new Type[1] { typeof(RectTransform) }); val.transform.SetParent(parent, false); TextMeshProUGUI obj = val.AddComponent<TextMeshProUGUI>(); ((TMP_Text)obj).fontSize = size; ((TMP_Text)obj).alignment = alignment; ((Graphic)obj).raycastTarget = false; ((TMP_Text)obj).enableWordWrapping = false; return obj; } private void Update() { UpdateButton(PauseMenu.Paused); UpdateBadge(); } private void UpdateButton(bool paused) { //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0143: 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_0158: Unknown result type (might be due to invalid IL or missing references) bool isActive = RelicRerollSession.IsActive; bool flag = !isActive && ShopRerollSession.IsActive; bool flag2 = (isActive || flag) && !paused; if (_panel.activeSelf != flag2) { _panel.SetActive(flag2); } if (flag2) { UseGameFont((TMP_Text)(object)_label); bool flag3 = (isActive ? RelicRerollSession.CanRerollNow : ShopRerollSession.CanRerollNow); ((Selectable)_button).interactable = flag3; ((Graphic)_panelImage).raycastTarget = flag3; ((Graphic)_icon).color = (Color)(flag3 ? Color.white : new Color(0.55f, 0.55f, 0.55f, 0.75f)); string text = (isActive ? RelicRerollSession.DisabledReason : ShopRerollSession.DisabledReason); string buttonText; if (text != null) { buttonText = $"Rerolls - {RerollState.Count} ({text})"; } else if (isActive) { buttonText = $"Reroll ({RerollState.Count}) [{KeyHint()}]"; } else { int remainingInShop = ShopRerollSession.RemainingInShop; string arg = ((remainingInShop == int.MaxValue) ? "" : $", {remainingInShop} left in this shop"); buttonText = $"Reroll shop relics ({RerollState.Count}{arg}) [{KeyHint()}]"; } SetButtonText(buttonText); KeyboardShortcut value = Plugin.RerollHotkey.Value; if (((KeyboardShortcut)(ref value)).IsDown() || IsButtonDown(Plugin.ControllerRerollButton.Value)) { Reroll(); } } } private static bool IsButtonDown(KeyboardShortcut shortcut) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) return ShortcutRules.IsDown(((KeyboardShortcut)(ref shortcut)).MainKey, ((KeyboardShortcut)(ref shortcut)).Modifiers, (Func<KeyCode, bool>)Input.GetKeyDown, (Func<KeyCode, bool>)Input.GetKey); } private unsafe string KeyHint() { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0078: 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) //IL_008c: Unknown result type (might be due to invalid IL or missing references) if (Time.unscaledTime >= _nextControllerCheck) { _nextControllerCheck = Time.unscaledTime + 1f; _controllerConnected = Input.GetJoystickNames().Any((string name) => !string.IsNullOrEmpty(name)); } KeyboardShortcut value = Plugin.RerollHotkey.Value; string text = ((object)(*(KeyboardShortcut*)(&value))/*cast due to .constrained prefix*/).ToString(); value = Plugin.ControllerRerollButton.Value; KeyCode mainKey = ((KeyboardShortcut)(ref value)).MainKey; if (!_controllerConnected || (int)mainKey == 0) { return text; } return text + " / " + ControllerButtonName(mainKey); } private unsafe static string ControllerButtonName(KeyCode button) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected I4, but got Unknown return (button - 330) switch { 0 => "A", 1 => "B", 2 => "X", 3 => "Y", 4 => "LB", 5 => "RB", 6 => "View", 7 => "Menu", 8 => "L3", 9 => "R3", _ => ((object)(*(KeyCode*)(&button))/*cast due to .constrained prefix*/).ToString(), }; } private static void Reroll() { if (RerollRules.DebouncePassed(Time.unscaledTimeAsDouble, _lastRerollTime)) { _lastRerollTime = Time.unscaledTimeAsDouble; if (RelicRerollSession.IsActive) { RelicRerollSession.TryReroll(); } else if (ShopRerollSession.IsActive) { ShopRerollSession.TryReroll(); } } } private void SetButtonText(string text) { //IL_0031: 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_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) if (!(((TMP_Text)_label).text == text)) { ((TMP_Text)_label).text = text; float x = ((TMP_Text)_label).GetPreferredValues(text, float.PositiveInfinity, 72f).x; float num = Mathf.Clamp(x + 62f + 48f, 380f, 1200f); _panelRect.sizeDelta = new Vector2(num, 72f); float num2 = Mathf.Min(x, num - 62f - 48f); float num3 = (0f - (62f + num2)) / 2f; ((Graphic)_icon).rectTransform.anchoredPosition = new Vector2(num3 + 24f, 0f); ((TMP_Text)_label).rectTransform.sizeDelta = new Vector2(num2, 72f); ((TMP_Text)_label).rectTransform.anchoredPosition = new Vector2(num3 + 62f + num2 / 2f, 0f); } } private void UpdateBadge() { string text = ((!Plugin.ShowRerollBadge.Value) ? "turned off in config" : RelicBarHiddenReason()); if (text == null && (Object)(object)((Component)_relicUI).transform.parent == (Object)null) { text = "relic bar has no parent to attach to"; } if (text != null) { if (_lastBadgeState != text) { _lastBadgeState = text; ModLog.Info("Reroll badge: hidden (" + text + ")"); } if ((Object)(object)_badge != (Object)null && _badge.activeSelf) { _badge.SetActive(false); } return; } Transform parent = ((Component)_relicUI).transform.parent; if ((Object)(object)_badge == (Object)null || (Object)(object)_badge.transform.parent != (Object)(object)parent) { BuildBadge(parent); } if (!_badge.activeSelf) { _badge.SetActive(true); } if (_badge.transform.GetSiblingIndex() != parent.childCount - 1) { _badge.transform.SetAsLastSibling(); } if (_lastBadgeState != "shown" || (Object)(object)_lastBadgeParent != (Object)(object)parent) { _lastBadgeState = "shown"; _lastBadgeParent = parent; ModLog.Info("Reroll badge: shown, attached to " + PathOf(parent)); } PositionBadge(); if (!_badgeStyled && Time.unscaledTime >= _nextStyleAttempt) { UseGameFont((TMP_Text)(object)_badgeCount); UseGameFont((TMP_Text)(object)_badgeCountShadow); _badgeStyled = CopyRelicCountdownStyle(); _nextStyleAttempt = Time.unscaledTime + 0.5f; } string text2 = RerollState.Count.ToString(); ((TMP_Text)_badgeCount).text = text2; ((TMP_Text)_badgeCountShadow).text = text2; } private string RelicBarHiddenReason() { if ((Object)(object)_relicUI == (Object)null || !((Behaviour)_relicUI).isActiveAndEnabled) { if (Time.unscaledTime < _nextRelicUISearch) { if (!((Object)(object)_relicUI == (Object)null)) { return "relic bar inactive"; } return "no relic bar in this scene"; } _nextRelicUISearch = Time.unscaledTime + 0.5f; RelicUI val = ((IEnumerable<RelicUI>)Object.FindObjectsOfType<RelicUI>()).FirstOrDefault((Func<RelicUI, bool>)((RelicUI relicUI) => ((Behaviour)relicUI).isActiveAndEnabled)); if ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)_relicUI) { _relicUI = val; ModLog.Info("Reroll badge: using relic bar " + PathOf(((Component)val).transform)); } if ((Object)(object)_relicUI == (Object)null) { return "no relic bar in this scene"; } if (!((Behaviour)_relicUI).isActiveAndEnabled) { return "relic bar inactive"; } } Canvas componentInParent = ((Component)_relicUI).GetComponentInParent<Canvas>(); if ((Object)(object)componentInParent != (Object)null && !((Behaviour)componentInParent).enabled) { return "relic bar canvas disabled"; } float num = EffectiveAlpha(((Component)_relicUI).transform); if (!(num > 0.05f)) { return $"relic bar faded out, alpha {num:0.00}"; } return null; } private static string PathOf(Transform transform) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) string text = ((Object)transform).name; Transform parent = transform.parent; while ((Object)(object)parent != (Object)null) { text = ((Object)parent).name + "/" + text; parent = parent.parent; } string text2 = text; Scene scene = ((Component)transform).gameObject.scene; return text2 + " (scene " + ((Scene)(ref scene)).name + ")"; } private static float EffectiveAlpha(Transform transform) { float num = 1f; Transform val = transform; while ((Object)(object)val != (Object)null) { CanvasGroup component = ((Component)val).GetComponent<CanvasGroup>(); if (!((Object)(object)component == (Object)null) && ((Behaviour)component).enabled) { num *= component.alpha; if (component.ignoreParentGroups) { break; } } val = val.parent; } return num; } private void PositionBadge() { //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: 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_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: 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_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0108: 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_007d: Expected O, but got Unknown //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_015a: 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_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_01e2: Unknown result type (might be due to invalid IL or missing references) //IL_01f4: Unknown result type (might be due to invalid IL or missing references) //IL_0206: Unknown result type (might be due to invalid IL or missing references) //IL_0219: Unknown result type (might be due to invalid IL or missing references) //IL_021e: Unknown result type (might be due to invalid IL or missing references) Transform parent = _badge.transform.parent; RectTransform val = (RectTransform)(object)((parent is RectTransform) ? parent : null); if ((Object)(object)_relicUI == (Object)null || (Object)(object)val == (Object)null) { return; } Transform transform = ((Component)_relicUI).transform; RectTransform val2 = (RectTransform)(object)((transform is RectTransform) ? transform : null); for (int i = 0; i < ((Component)_relicUI).transform.childCount; i++) { Transform child = ((Component)_relicUI).transform.GetChild(i); if (((Component)child).gameObject.activeInHierarchy && (Object)(object)((Component)child).GetComponent<RelicIcon>() != (Object)null) { val2 = (RectTransform)child; break; } } if (!((Object)(object)val2 == (Object)null)) { val2.GetWorldCorners(_corners); Vector2 val3 = Vector2.op_Implicit(((Transform)val).InverseTransformPoint(_corners[1])); Vector2 val4 = Vector2.op_Implicit(((Transform)val).InverseTransformPoint(_corners[2])); Vector2 val5 = Vector2.op_Implicit(((Transform)val).InverseTransformPoint(_corners[0])); float num = Mathf.Min(val4.x - val3.x, val3.y - val5.y); if (!(num <= 0f)) { float num2 = num * 0.12f; ((Transform)_badgeRect).localScale = Vector3.one; _badgeRect.sizeDelta = new Vector2(num, num); ((Transform)_badgeRect).localPosition = new Vector3(val3.x, val3.y + num2, 0f); float fontSize = CountFontSize(num); ((TMP_Text)_badgeCount).fontSize = fontSize; ((TMP_Text)_badgeCountShadow).fontSize = fontSize; Vector2 sizeDelta = default(Vector2); ((Vector2)(ref sizeDelta))..ctor(num * 0.95f, num * 0.7f); Vector2 val6 = default(Vector2); ((Vector2)(ref val6))..ctor((0f - num) * 0.02f, num * 0.02f); ((TMP_Text)_badgeCount).rectTransform.sizeDelta = sizeDelta; ((TMP_Text)_badgeCountShadow).rectTransform.sizeDelta = sizeDelta; ((TMP_Text)_badgeCount).rectTransform.anchoredPosition = val6; ((TMP_Text)_badgeCountShadow).rectTransform.anchoredPosition = val6 + new Vector2(num * 0.03f, (0f - num) * 0.03f); } } } private float CountFontSize(float size) { int num; if (!(Mathf.Abs(size - _countMeasuredAt) > 0.5f)) { num = (((Object)(object)_countMeasuredFont != (Object)(object)((TMP_Text)_badgeCount).font) ? 1 : 0); if (num == 0) { goto IL_0037; } } else { num = 1; } _countCalibrationTries = 0; goto IL_0037; IL_0037: if (num != 0 || !_countCalibrated) { CalibrateCountFont(size); } return Mathf.Max(1f, size * _countFontScale); } private void CalibrateCountFont(float size) { //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) _countMeasuredAt = size; _countMeasuredFont = ((TMP_Text)_badgeCount).font; _countCalibrated = false; _countFontScale = 0.5f; if (string.IsNullOrEmpty(((TMP_Text)_badgeCount).text)) { return; } ((TMP_Text)_badgeCount).fontSize = size; ((TMP_Text)_badgeCount).ForceMeshUpdate(false, false); Bounds textBounds = ((TMP_Text)_badgeCount).textBounds; float y = ((Bounds)(ref textBounds)).size.y; if (y <= 0.01f) { if (++_countCalibrationTries >= 3) { _countCalibrated = true; } return; } _countCalibrationTries = 0; _countFontScale = Mathf.Clamp(0.44f * size / y, 0.1f, 1.5f); _countCalibrated = true; object[] obj = new object[4] { y, size, _countFontScale, null }; TMP_FontAsset font = ((TMP_Text)_badgeCount).font; obj[3] = ((font != null) ? ((Object)font).name : null); ModLog.Info(string.Format("Reroll badge: count {0:0.#} tall at font size {1:0.#}, scale {2:0.00} ({3})", obj)); } private bool CopyRelicCountdownStyle() { //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: 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_00dd: Unknown result type (might be due to invalid IL or missing references) RelicIcon[] array = Object.FindObjectsOfType<RelicIcon>(); foreach (RelicIcon val in array) { if (!((Object)(object)val.countdownText == (Object)null)) { ((TMP_Text)_badgeCount).font = (((Object)(object)val.countdownText.font != (Object)null) ? val.countdownText.font : TMP_Settings.defaultFontAsset); Color color = ((Graphic)val.countdownText).color; color.a = 1f; ((Graphic)_badgeCount).color = color; Material fontSharedMaterial = val.countdownText.fontSharedMaterial; if ((Object)(object)fontSharedMaterial != (Object)null) { ((TMP_Text)_badgeCount).fontSharedMaterial = fontSharedMaterial; } ((TMP_Text)_badgeCountShadow).font = ((TMP_Text)_badgeCount).font; if ((Object)(object)val.countdownTextDropShadow != (Object)null) { Color color2 = ((Graphic)val.countdownTextDropShadow).color; color2.a = 1f; ((Graphic)_badgeCountShadow).color = color2; } return true; } } return false; } private static void UseGameFont(TMP_Text target) { if ((Object)(object)target.font != (Object)null && (Object)(object)target.font != (Object)(object)TMP_Settings.defaultFontAsset) { return; } TextMeshProUGUI[] array = Object.FindObjectsOfType<TextMeshProUGUI>(); foreach (TextMeshProUGUI val in array) { if ((Object)(object)val != (Object)(object)target && (Object)(object)((TMP_Text)val).font != (Object)null && (Object)(object)((TMP_Text)val).font != (Object)(object)TMP_Settings.defaultFontAsset) { target.font = ((TMP_Text)val).font; break; } } } } [HarmonyPatch(typeof(RelicUI), "OnEnable")] internal static class RelicBarEnablePatch { private static void Postfix() { RerollHud.Ensure(); } } internal static class RerollState { private static readonly RerollCounter Counter = new RerollCounter(); private static string FilePath => Path.Combine(Paths.ConfigPath, "PeglinRelicRerolls.state"); public static int Count => Counter.Count; public static int Used => Counter.Used; public static void ResetForNewRun(CruciballManager cruciball) { int num = (((Object)(object)cruciball != (Object)null) ? cruciball.currentCruciballLevel : 0); Counter.Reset(StartingCountFor(num)); ModLog.Info($"New run (cruciball {num}): rerolls set to {Count}"); } public static void Add(int amount) { Counter.Add(amount); } public static bool TrySpend() { return Counter.TrySpend(); } public static void Refund() { Counter.Refund(); } public static int StartingCountFor(int cruciballLevel) { return RerollRules.StartingCount(Plugin.StartingRerolls.Value, Plugin.CruciballPenaltyPerLevel.Value, RerollRules.ParsePenaltyLevels(Plugin.CruciballPenaltyLevels.Value), cruciballLevel); } public static void Commit() { try { File.WriteAllText(FilePath, Counter.Snapshot(StaticGameData.currentSeed).Serialize()); string path = Path.Combine(Paths.ConfigPath, "PeglinRerolls.state"); if (File.Exists(path)) { File.Delete(path); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not write " + FilePath + ": " + ex.Message)); } } public static void Restore() { string text = null; try { string text2 = Path.Combine(Paths.ConfigPath, "PeglinRerolls.state"); string path = (File.Exists(FilePath) ? FilePath : text2); if (File.Exists(path)) { text = File.ReadAllText(path); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not read " + FilePath + ": " + ex.Message)); } CruciballManager val = Resources.FindObjectsOfTypeAll<CruciballManager>().FirstOrDefault(); int startingCount = StartingCountFor(((Object)(object)val != (Object)null) ? val.currentCruciballLevel : 0); string currentSeed = StaticGameData.currentSeed; RerollSnapshot snapshot = RerollSnapshot.ResolveOnContinue(text, currentSeed, startingCount); Counter.Restore(snapshot); if (RerollSnapshot.TryParse(text, out var snapshot2) && snapshot2.Seed == currentSeed) { ModLog.Info($"Continued run: rerolls restored to {Count}"); } else { Plugin.Log.LogWarning((object)$"No saved rerolls for this run; using starting count {Count}"); } } } internal static class RerollTooltip { public const string Title = "Reroll"; public const string Description = "Gain <color=#3A9A2A>1</color> <color=#D2662E>Reroll</color>. Use it to swap the relics offered after minibosses and bosses, in chests, or in the shop."; private static Relic _relic; public static Relic Relic { get { if ((Object)(object)_relic == (Object)null) { _relic = ScriptableObject.CreateInstance<Relic>(); ((Object)_relic).name = "PeglinRelicRerollsReroll"; _relic.locKey = "peglinrerolls_reroll"; _relic.sprite = DieSprite.Get(); ((Object)_relic).hideFlags = (HideFlags)32; } return _relic; } } } [HarmonyPatch(typeof(Tooltip), "Initialize", new Type[] { typeof(Relic) })] internal static class RelicTooltipPatch { private static bool Prefix(Tooltip __instance, Relic relic) { Localize component = ((Component)__instance.descriptionText).GetComponent<Localize>(); if ((Object)(object)relic == (Object)null || (Object)(object)relic != (Object)(object)RerollTooltip.Relic) { if ((Object)(object)component != (Object)null) { ((Behaviour)component).enabled = true; } return true; } if ((Object)(object)component != (Object)null) { ((Behaviour)component).enabled = false; } ((Component)__instance.nameText).gameObject.SetActive(true); __instance.damageInfo.gameObject.SetActive(false); ((TMP_Text)__instance.nameText).text = "Reroll"; ((TMP_Text)__instance.descriptionText).text = "Gain <color=#3A9A2A>1</color> <color=#D2662E>Reroll</color>. Use it to swap the relics offered after minibosses and bosses, in chests, or in the shop."; TooltipKeywordDescriptions keywordDesc = __instance.keywordDesc; if (keywordDesc != null) { keywordDesc.UpdateString("none"); } Traverse.Create((object)__instance).Field("_keywordDescCount").SetValue((object)0); ((Component)__instance.cruciballIndicator).gameObject.SetActive(false); return false; } } [HarmonyPatch(typeof(GameInit), "Start")] internal static class NewRunPatch { private static void Prefix(GameInit __instance, CruciballManager ____cruciballManager) { if (Traverse.Create((object)__instance).Field("LoadData").Field("NewGame") .GetValue<bool>()) { RerollState.ResetForNewRun(____cruciballManager); RelicRerollSession.EndInstance(); } } } [HarmonyPatch(typeof(MapController), "Start")] internal static class NewRunMapPatch { private static void Prefix(MapController __instance, CruciballManager ____cruciballManager) { if (Traverse.Create((object)__instance).Field("_loadMapData").Field("NewGame") .GetValue<bool>()) { RerollState.ResetForNewRun(____cruciballManager); RelicRerollSession.EndInstance(); } } } [HarmonyPatch(typeof(MapController), "SaveData")] internal static class RunSavePatch { private static void Postfix() { RerollState.Commit(); RelicRerollSession.EndInstance(); } } [HarmonyPatch(typeof(SaveManager), "RequestLoad")] internal static class RunLoadPatch { private static void Postfix() { RerollState.Restore(); } } internal class PurchasableReroll : IPurchasableItem { private readonly int _cost; public PurchasableReroll(int cost) { _cost = cost; } public void PurchaseItem() { RerollState.Add(1); ModLog.Info($"Bought a reroll. Rerolls: {RerollState.Count}"); } public Sprite GetPurchasableImage() { return DieSprite.Get(); } public Vector3 GetSpriteLocalScale() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return Vector3.one; } public Color GetSpriteColor() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return Color.white; } public int GetCost() { return _cost; } public void ShowTooltip(Vector3 position, Vector3 offset, bool followMousePosition = false) { //IL_000a: 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) TooltipManager.Instance.ShowTooltipRelic(RerollTooltip.Relic, position, offset, followMousePosition); } public bool GetUnlocked() { return true; } } internal static class ShopRerollSession { private readonly struct ShopRelicSlot { public ShopItem ShopItem { get; } public PurchasableRelic Item { get; } public Relic Relic { get; } public bool ShopOnly { get; } public RelicRarity Rarity { get; } public ShopRelicSlot(ShopItem shopItem, PurchasableRelic item, Relic relic, bool shopOnly, RelicRarity rarity) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) ShopItem = shopItem; Item = item; Relic = relic; ShopOnly = shopOnly; Rarity = rarity; } } private static readonly RelicScreenInstance<Relic> Instance = new RelicScreenInstance<Relic>(); private static ShopManager _shop; private static bool _anySlotCanChange; private static bool _hasUnboughtRelics; public static bool IsActive { get { if (!Plugin.EnableShopRelicReroll.Value || (Object)(object)_shop == (Object)null || !ShopManager.isOpen || PauseMenu.Paused || GameBlockingWindow.windowOpen) { return false; } Traverse val = Traverse.Create((object)_shop); bool flag = ((Object)(object)_shop.upgradeConfirmationRelic != (Object)null && ((Component)_shop.upgradeConfirmationRelic).gameObject.activeInHierarchy) || ((Object)(object)_shop.upgradeConfirmationOrbInspect != (Object)null && ((Component)_shop.upgradeConfirmationOrbInspect).gameObject.activeInHierarchy); if (!val.Field("_storeClosed").GetValue<bool>() && !flag) { return !val.Field("inventoryOpen").GetValue<bool>(); } return false; } } public static int RemainingInShop => RerollRules.RemainingUnderLimit(Instance.RerollsUsed, Plugin.ShopRerollLimit.Value); public static bool CanRerollNow { get { if (RerollState.Count > 0 && RemainingInShop > 0) { return _anySlotCanChange; } return false; } } public static string DisabledReason { get { if (RemainingInShop <= 0) { if (Plugin.ShopRerollLimit.Value != 0) { return $"disabled, this shop's {Plugin.ShopRerollLimit.Value} rerolls have been used"; } return "disabled in shops"; } if (!_hasUnboughtRelics) { return "no relics left in this shop to reroll"; } if (!_anySlotCanChange) { return "disabled, no more relics to reroll into"; } return null; } } public static void Begin(ShopManager shop, Relic removedForDie) { _shop = shop; Instance.Enter(shop); Instance.MarkSeen(from slot in ShopRelics() select slot.Relic); if ((Object)(object)removedForDie != (Object)null) { Instance.MarkSeen((IEnumerable<Relic>)(object)new Relic[1] { removedForDie }); } Refresh(); ModLog.Info($"Shop opened: {ShopRelics().Count} relic(s) can be rerolled."); } public static void Refresh() { //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0104: 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) List<ShopRelicSlot> list = ShopRelics(); _hasUnboughtRelics = list.Count > 0; _anySlotCanChange = false; if ((Object)(object)_shop == (Object)null || list.Count == 0) { return; } RelicManager relicManager = _shop.relicManager; Relic[] exclude = list.Select((ShopRelicSlot slot) => slot.Relic).ToArray(); foreach (IGrouping<bool, ShopRelicSlot> item in from slot in list group slot by slot.ShopOnly) { if (item.Key) { _anySlotCanChange |= RelicPool.AvailableShopOnly(relicManager, exclude).Count > 0; continue; } foreach (RelicRarity item2 in item.Select((ShopRelicSlot slot) => slot.Rarity).Distinct()) { int unseenInPool = RelicPool.CountAvailable(relicManager, item2, Instance.Seen); int notOnScreenInPool = RelicPool.CountAvailable(relicManager, item2, exclude); _anySlotCanChange |= RerollRules.AvailableToReroll(unseenInPool, notOnScreenInPool) > 0; } } } public static void TryReroll() { //IL_00ed: Unknown result type (might be due to invalid IL or missing references) if (!IsActive || !CanRerollNow || !RerollState.TrySpend()) { UIAudioManager.InvalidSelectionSfxRequested?.Invoke(); return; } RelicManager relicManager = _shop.relicManager; List<ShopRelicSlot> list = ShopRelics(); Relic[] array = list.Select((ShopRelicSlot slot) => slot.Relic).ToArray(); if (!list.Any((ShopRelicSlot slot) => (!slot.ShopOnly) ? (RelicPool.CountAvailable(relicManager, slot.Rarity, Instance.Seen) > 0) : (RelicPool.AvailableShopOnly(relicManager, Instance.Seen).Count > 0))) { Instance.RecycleSeen(array); ModLog.Info("Shop do-not-offer list was full: recycled it, keeping only the relics on the shelf."); } List<Relic> list2 = new List<Relic>(); int num = 0; TooltipManager instance = TooltipManager.Instance; if (instance != null) { instance.HideTooltip(); } foreach (ShopRelicSlot item in list) { IEnumerable<Relic> exclude = Instance.Seen.Concat(array).Concat(list2); Relic val = (Relic)(item.ShopOnly ? ((object)RelicPool.DrawShopOnly(relicManager, exclude)) : ((object)RelicPool.Draw(relicManager, item.Rarity, 1, exclude).FirstOrDefault())); if (!((Object)(object)val == (Object)null)) { ReplaceRelic(item, val); RerollAnimation.FlipIn(((Object)(object)item.ShopItem.itemImage != (Object)null) ? ((Component)item.ShopItem.itemImage).transform : ((Component)item.ShopItem).transform, num); list2.Add(val); num++; } } if (num == 0) { RerollState.Refund(); UIAudioManager.InvalidSelectionSfxRequested?.Invoke(); ModLog.Info("Shop reroll refunded: no new relics to offer."); Refresh(); } else { RerollAnimation.PlaySound(); Instance.MarkSeen(list2); Instance.RecordReroll(); Refresh(); ModLog.Info(string.Format("Shop reroll: {0} of {1} relic(s) changed: {2}. ", num, list.Count, string.Join(", ", list2.Select(RelicPool.DisplayName))) + string.Format("Rerolls left: {0}, shop rerolls left here: {1}", RerollState.Count, (RemainingInShop == int.MaxValue) ? "unlimited" : RemainingInShop.ToString())); ModLog.Info(string.Format("Shop do-not-offer list ({0}): {1}", Instance.Seen.Count, string.Join(", ", Instance.Seen.Select(RelicPool.DisplayName)))); } } private static void ReplaceRelic(ShopRelicSlot slot, Relic relic) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown //IL_0132: Unknown result type (might be due to invalid IL or missing references) Traverse val = Traverse.Create((object)slot.Item); PurchasableRelic val2 = new PurchasableRelic(relic, _shop.relicManager, val.Field("_costMultiplier").GetValue<int>()); Traverse obj = Traverse.Create((object)val2); obj.Field("_cost").SetValue((object)val.Field("_cost").GetValue<int>()); obj.Field("_numPriceIncreases").SetValue((object)val.Field("_numPriceIncreases").GetValue<int>()); Component component = ((Component)slot.ShopItem).GetComponent("MouseOverDetectorPointerHandler"); if ((Object)(object)component != (Object)null) { Traverse.Create((object)component).Field("OnPointerEntered").SetValue((object)null); Traverse.Create((object)component).Field("OnPointerExited").SetValue((object)null); } SelectionEventListener componentInChildren = ((Component)slot.ShopItem).GetComponentInChildren<SelectionEventListener>(true); if ((Object)(object)componentInChildren != (Object)null) { Traverse.Create((object)componentInChildren).Field("OnSelectionEvent").SetValue((object)null); Traverse.Create((object)componentInChildren).Field("OnDeselectionEvent").SetValue((object)null); } if ((Object)(object)slot.ShopItem.itemBackground != (Object)null) { slot.ShopItem.itemBackground.transform.localRotation = Quaternion.identity; } Traverse val3 = Traverse.Create((object)_shop); slot.ShopItem.Initialize((IPurchasableItem)(object)val2, _shop, val3.Field("_playerHealthController").GetValue<PlayerHealthController>(), _shop.relicManager); IPurchasableItem[] value = val3.Field("_purchasableRelics").GetValue<IPurchasableItem[]>(); int num = Array.IndexOf(value, (IPurchasableItem)(object)slot.Item); if (num >= 0) { value[num] = (IPurchasableItem)(object)val2; } } private static List<ShopRelicSlot> ShopRelics() { //IL_00d5: Unknown result type (might be due to invalid IL or missing references) List<ShopRelicSlot> list = new List<ShopRelicSlot>(); List<ShopItem> list2 = (((Object)(object)_shop != (Object)null) ? Traverse.Create((object)_shop).Field("relicItems").GetValue<List<ShopItem>>() : null); if (list2 == null) { return list; } foreach (ShopItem item in list2) { if ((Object)(object)item == (Object)null || Traverse.Create((object)item).Field("_purchased").GetValue<bool>()) { continue; } IPurchasableItem value = Traverse.Create((object)item).Field("_item").GetValue<IPurchasableItem>(); PurchasableRelic val = (PurchasableRelic)(object)((value is PurchasableRelic) ? value : null); if (val != null) { Relic value2 = Traverse.Create((object)val).Field("_relic").GetValue<Relic>(); if ((Object)(object)value2 != (Object)null) { bool shopOnly = _shop.relicManager.IsRelicShopOnlyRelic(value2); list.Add(new ShopRelicSlot(item, val, value2, shopOnly, RelicPool.RarityOf(_shop.relicManager, value2))); } } } return list; } } [HarmonyPatch(typeof(ShopManager), "SetUpRelicOffer")] internal static class ShopRelicOfferPatch { private static void Postfix(ShopManager __instance, List<ShopItem> ___relicItems, IPurchasableItem[] ____purchasableRelics, PlayerHealthController ____playerHealthController) { RerollHud.Ensure(); Relic removedForDie = (Plugin.EnableShopReroll.Value ? PlaceDie(__instance, ___relicItems, ____purchasableRelics, ____playerHealthController) : null); ShopRerollSession.Begin(__instance, removedForDie); } private static Relic PlaceDie(ShopManager shop, List<ShopItem> relicItems, IPurchasableItem[] purchasableRelics, PlayerHealthController playerHealthController) { RelicManager relicManager = shop.relicManager; List<ShopSlotKind> list = relicItems.Select((ShopItem item) => KindOf(relicManager, item)).ToList(); ModLog.Info("Shop relic slots: " + string.Join(", ", list.Select((ShopSlotKind kind, int i) => $"{i + 1}={kind}"))); int num = RerollRules.DieSlot(list); if (num < 0) { ModLog.Info("Shop has no common or rare relic to swap for the reroll die; no die this time."); return null; } ShopItem val = relicItems[num]; IPurchasableItem value = Traverse.Create((object)val).Field("_item").GetValue<IPurchasableItem>(); Relic val2 = ((value is PurchasableRelic) ? Traverse.Create((object)value).Field("_relic").GetValue<Relic>() : null); int num2 = Array.IndexOf(purchasableRelics, value); ArrowSelection componentInChildren = ((Component)val).GetComponentInChildren<ArrowSelection>(); ShopItem component = Object.Instantiate<GameObject>(shop.purchasablePrefab, shop.relicContainer.transform).GetComponent<ShopItem>(); ((Component)component).transform.SetSiblingIndex(((Component)val).transform.GetSiblingIndex()); ((Component)val).gameObject.SetActive(false); Object.Destroy((Object)(object)((Component)val).gameObject); bool act = (Object)(object)MapController.instance != (Object)null && MapController.instance.Act == 4; int num3 = RerollRules.ShopRerollCost(Plugin.ShopRerollCost.Value, relicManager.WandOfGreedEffectActive(), act); PurchasableReroll purchasableReroll = new PurchasableReroll(num3); component.Initialize((IPurchasableItem)(object)purchasableReroll, shop, playerHealthController, relicManager); ArrowSelection componentInChildren2 = ((Component)component).GetComponentInChildren<ArrowSelection>(); if ((Object)(object)componentInChildren2 != (Object)null && (Object)(object)componentInChildren != (Object)null) { Traverse.Create((object)componentInChildren2).Field("rewiredEventSystem").SetValue(Traverse.Create((object)componentInChildren).Field("rewiredEventSystem").GetValue()); } relicItems[num] = component; if (num2 >= 0) { purchasableRelics[num2] = (IPurchasableItem)(object)purchasableReroll; } ModLog.Info($"Reroll die placed in shop slot {num + 1} for {num3}g, replacing {RelicPool.DisplayName(val2)}."); return val2; } private static ShopSlotKind KindOf(RelicManager relicManager, ShopItem shopItem) { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Invalid comparison between Unknown and I4 //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Invalid comparison between Unknown and I4 IPurchasableItem value = Traverse.Create((object)shopItem).Field("_item").GetValue<IPurchasableItem>(); PurchasableRelic val = (PurchasableRelic)(object)((value is PurchasableRelic) ? value : null); if (val == null) { return ShopSlotKind.Other; } Relic value2 = Traverse.Create((object)val).Field("_relic").GetValue<Relic>(); if ((Object)(object)value2 == (Object)null) { return ShopSlotKind.Other; } if (relicManager.IsRelicShopOnlyRelic(value2)) { return ShopSlotKind.ShopOnly; } RelicRarity val2 = RelicPool.RarityOf(relicManager, value2); if ((int)val2 != 2) { if ((int)val2 == 3) { return ShopSlotKind.Boss; } return ShopSlotKind.Common; } return ShopSlotKind.Rare; } } [HarmonyPatch(typeof(ShopItem), "PurchaseItem")] internal static class ShopPurchasePatch { private static void Postfix() { ShopRerollSession.Refresh(); } } [HarmonyPatch(typeof(ShopItem), "ShowUpgradeInformation")] internal static class ShopItemClickPatch { private static bool Prefix(ShopItem __instance, IPurchasableItem ____item) { if (____item is PurchasableReroll) { __instance.PurchaseItem(); return false; } return true; } } } namespace PeglinRelicRerolls.Core { public class RelicScreenInstance<T> { private readonly HashSet<T> _seen = new HashSet<T>(); private readonly List<T> _seenInOrder = new List<T>(); private object _screenKey; private bool _started; public int RerollsUsed { get; private set; } public IReadOnlyList<T> Seen => _seenInOrder; public void Enter(object screenKey) { if (!_started || !object.Equals(_screenKey, screenKey)) { Clear(); _screenKey = screenKey; _started = true; } } public void Clear() { _seen.Clear(); _seenInOrder.Clear(); RerollsUsed = 0; _screenKey = null; _started = false; } public void MarkSeen(IEnumerable<T> items) { foreach (T item in items) { if (item != null && _seen.Add(item)) { _seenInOrder.Add(item); } } } public bool HasSeen(T item) { return _seen.Contains(item); } public void RecycleSeen(IEnumerable<T> keep) { _seen.Clear(); _seenInOrder.Clear(); MarkSeen(keep); } public bool OffersSomethingNew(IEnumerable<T> offer) { foreach (T item in offer) { if (item != null && !_seen.Contains(item)) { return true; } } return false; } public void RecordReroll() { RerollsUsed++; } } public class RerollCounter { public int Count { get; private set; } public int Used { get; private set; } public void Reset(int startingCount) { Count = Math.Max(0, startingCount); Used = 0; } public void Add(int amount) { if (amount < 0) { throw new ArgumentOutOfRangeException("amount", "Use TrySpend to remove rerolls."); } Count += amount; } public bool TrySpend() { if (Count <= 0) { return false; } Count--; Used++; return true; } public void Refund() { if (Used > 0) { Count++; Used--; } } public RerollSnapshot Snapshot(string seed) { return new RerollSnapshot(seed, Count, Used); } public void Restore(RerollSnapshot snapshot) { Count = Math.Max(0, snapshot.Count); Used = Math.Max(0, snapshot.Used); } } public enum ChestTier { Common, Rare, Boss } public enum ShopSlotKind { Common, Rare, Boss, ShopOnly, Other } public static class RerollRules { public const double RerollDebounceSeconds = 0.2; public static int DieSlot(IRead