using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Photon.Pun;
using UnityEngine;
using UnityEngine.SceneManagement;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("QuotaReroll")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("QuotaReroll")]
[assembly: AssemblyCopyright("Copyright © 2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("d9c049c8-f290-4a04-853c-55008cb1c07d")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace QuickRejoin;
[BepInPlugin("com.myself.quickrejoin", "Quick Rejoin Mod", "3.3.0")]
public class Plugin : BaseUnityPlugin
{
internal static ManualLogSource Log;
private void Awake()
{
//IL_0021: Unknown result type (might be due to invalid IL or missing references)
//IL_0027: Expected O, but got Unknown
Log = ((BaseUnityPlugin)this).Logger;
Log.LogInfo((object)"=== Quick Rejoin v3.3.0 Loaded ===");
GameObject val = new GameObject("QuickRejoinManager");
Object.DontDestroyOnLoad((Object)(object)val);
((Object)val).hideFlags = (HideFlags)61;
val.AddComponent<RejoinManager>();
Log.LogInfo((object)"Quick Rejoin initialized");
}
}
internal class RejoinManager : MonoBehaviour
{
[CompilerGenerated]
private sealed class <WaitForRerollComplete>d__16 : IEnumerator<object>, IDisposable, IEnumerator
{
private int <>1__state;
private object <>2__current;
public RejoinManager <>4__this;
private float <waitTime>5__1;
private LevelGenerator <generator>5__2;
private FieldInfo <generatedField>5__3;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <WaitForRerollComplete>d__16(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<generator>5__2 = null;
<generatedField>5__3 = null;
<>1__state = -2;
}
private bool MoveNext()
{
//IL_0078: 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)
//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
//IL_01af: Expected O, but got Unknown
//IL_004b: Unknown result type (might be due to invalid IL or missing references)
//IL_0055: Expected O, but got Unknown
//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
//IL_00d5: Expected O, but got Unknown
Scene activeScene;
switch (<>1__state)
{
default:
return false;
case 0:
<>1__state = -1;
<waitTime>5__1 = 0f;
goto IL_0078;
case 1:
<>1__state = -1;
<waitTime>5__1 += 0.5f;
goto IL_0078;
case 2:
<>1__state = -1;
<waitTime>5__1 += 0.5f;
goto IL_00f8;
case 3:
{
<>1__state = -1;
<waitTime>5__1 += 0.5f;
goto IL_01d2;
}
IL_0078:
activeScene = SceneManager.GetActiveScene();
if (((Scene)(ref activeScene)).name != "Main" && <waitTime>5__1 < 30f)
{
<>2__current = (object)new WaitForSeconds(0.5f);
<>1__state = 1;
return true;
}
<generator>5__2 = null;
<waitTime>5__1 = 0f;
goto IL_00f8;
IL_01d2:
if (<waitTime>5__1 < 30f)
{
if (!(bool)<generatedField>5__3.GetValue(<generator>5__2))
{
<>2__current = (object)new WaitForSeconds(0.5f);
<>1__state = 3;
return true;
}
Plugin.Log.LogInfo((object)"Level generation complete!");
}
goto IL_01e6;
IL_01e6:
<generatedField>5__3 = null;
break;
IL_00f8:
if ((Object)(object)<generator>5__2 == (Object)null && <waitTime>5__1 < 30f)
{
<generator>5__2 = Object.FindObjectOfType<LevelGenerator>();
<>2__current = (object)new WaitForSeconds(0.5f);
<>1__state = 2;
return true;
}
if (!((Object)(object)<generator>5__2 != (Object)null))
{
break;
}
<generatedField>5__3 = typeof(LevelGenerator).GetField("Generated", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (<generatedField>5__3 != null)
{
<waitTime>5__1 = 0f;
goto IL_01d2;
}
goto IL_01e6;
}
<>4__this._isRerolling = false;
Plugin.Log.LogInfo((object)"Reroll complete!");
return false;
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
private const float CheckInterval = 0.5f;
private const float MaxWaitTime = 30f;
internal static bool RerollQueued;
internal static int QueuedSeed;
internal static bool IsShopReroll;
internal static bool SeedApplied;
internal static Dictionary<string, int> LevelStartHealth = new Dictionary<string, int>();
internal static bool HealthSaved = false;
private static Harmony _harmony;
private bool _isRerolling;
private void Awake()
{
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
//IL_0020: Expected O, but got Unknown
Plugin.Log.LogInfo((object)"RejoinManager Awake");
_harmony = new Harmony("com.myself.quickrejoin");
_harmony.PatchAll();
Plugin.Log.LogInfo((object)"Harmony patches applied");
Plugin.Log.LogInfo((object)"Reroll key: Backslash (\\)");
Plugin.Log.LogInfo((object)"Debug key: Equals (=)");
}
private void Update()
{
if (Input.GetKeyDown((KeyCode)92))
{
TryInitiateReroll();
}
if (Input.GetKeyDown((KeyCode)61))
{
PrintLevelDebug();
}
}
private void PrintLevelDebug()
{
Plugin.Log.LogInfo((object)"=== LEVEL DEBUG ===");
RunManager val = Object.FindObjectOfType<RunManager>();
if ((Object)(object)val == (Object)null)
{
Plugin.Log.LogInfo((object)"RunManager not found");
return;
}
FieldInfo field = typeof(RunManager).GetField("levelCurrent", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
FieldInfo field2 = typeof(RunManager).GetField("levelShop", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (field != null)
{
object? value = field.GetValue(val);
Object val2 = (Object)((value is Object) ? value : null);
Plugin.Log.LogInfo((object)("Current level: " + (((val2 != null) ? val2.name : null) ?? "null")));
}
if (field2 != null)
{
object? value2 = field2.GetValue(val);
Object val3 = (Object)((value2 is Object) ? value2 : null);
Plugin.Log.LogInfo((object)("Shop level: " + (((val3 != null) ? val3.name : null) ?? "null")));
}
Plugin.Log.LogInfo((object)$"Is Multiplayer: {PhotonNetwork.IsConnected}");
Plugin.Log.LogInfo((object)$"Is Master Client: {PhotonNetwork.IsMasterClient}");
Plugin.Log.LogInfo((object)$"Saved level start health (HealthSaved: {HealthSaved}):");
foreach (KeyValuePair<string, int> item in LevelStartHealth)
{
Plugin.Log.LogInfo((object)$" {item.Key}: {item.Value}");
}
if ((Object)(object)StatsManager.instance != (Object)null)
{
Plugin.Log.LogInfo((object)"StatsManager playerHealth:");
foreach (KeyValuePair<string, int> item2 in StatsManager.instance.playerHealth)
{
Plugin.Log.LogInfo((object)$" {item2.Key}: {item2.Value}");
}
}
Plugin.Log.LogInfo((object)"=== END DEBUG ===");
}
internal static void SaveLevelStartHealth()
{
if (HealthSaved)
{
Plugin.Log.LogInfo((object)"Health already saved for this level, skipping");
return;
}
LevelStartHealth.Clear();
if (!((Object)(object)StatsManager.instance != (Object)null))
{
return;
}
foreach (KeyValuePair<string, int> item in StatsManager.instance.playerHealth)
{
LevelStartHealth[item.Key] = item.Value;
Plugin.Log.LogInfo((object)$"Saved starting health for {item.Key}: {item.Value}");
}
HealthSaved = true;
}
private void RestoreHealthToLevelStart()
{
Plugin.Log.LogInfo((object)"Restoring health to level start values...");
if ((Object)(object)StatsManager.instance == (Object)null || LevelStartHealth.Count == 0)
{
Plugin.Log.LogWarning((object)"No saved health data to restore");
return;
}
foreach (KeyValuePair<string, int> item in LevelStartHealth)
{
StatsManager.instance.SetPlayerHealth(item.Key, item.Value, true);
Plugin.Log.LogInfo((object)$"Restored health for {item.Key}: {item.Value}");
}
HealthSaved = false;
}
private void TryInitiateReroll()
{
if (_isRerolling)
{
Plugin.Log.LogWarning((object)"Reroll already in progress");
return;
}
if (PhotonNetwork.IsConnected && !PhotonNetwork.IsMasterClient)
{
Plugin.Log.LogWarning((object)"Cannot reroll: Only host can reroll in multiplayer");
return;
}
RunManager val = Object.FindObjectOfType<RunManager>();
if ((Object)(object)val == (Object)null)
{
Plugin.Log.LogWarning((object)"Cannot reroll: RunManager not found");
return;
}
FieldInfo field = typeof(RunManager).GetField("levelCurrent", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
FieldInfo field2 = typeof(RunManager).GetField("levels", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
FieldInfo field3 = typeof(RunManager).GetField("levelShop", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (field == null || field2 == null || field3 == null)
{
Plugin.Log.LogWarning((object)"Cannot reroll: Could not read RunManager fields");
return;
}
object value = field.GetValue(val);
IList list = field2.GetValue(val) as IList;
object value2 = field3.GetValue(val);
if (value == null || list == null)
{
Plugin.Log.LogWarning((object)"Cannot reroll: Level data is null");
return;
}
bool flag = false;
bool flag2 = value == value2;
foreach (object item in list)
{
if (item == value)
{
flag = true;
break;
}
}
if (!flag && !flag2)
{
object obj = ((value is Object) ? value : null);
string text = ((obj != null) ? ((Object)obj).name : null) ?? "unknown";
Plugin.Log.LogWarning((object)("Cannot reroll: Not in a run level or shop (current: " + text + ")"));
return;
}
Plugin.Log.LogInfo((object)(">>> " + (flag2 ? "SHOP " : "") + "REROLL INITIATED <<<"));
int num = Random.Range(int.MinValue, int.MaxValue);
Plugin.Log.LogInfo((object)$"New seed: {num}");
QueuedSeed = num;
RerollQueued = true;
IsShopReroll = flag2;
SeedApplied = false;
_isRerolling = true;
RestoreHealthToLevelStart();
if (flag2)
{
val.ChangeLevel(false, false, (ChangeLevelType)5);
}
else
{
val.ChangeLevel(false, false, (ChangeLevelType)1);
}
((MonoBehaviour)this).StartCoroutine(WaitForRerollComplete());
}
[IteratorStateMachine(typeof(<WaitForRerollComplete>d__16))]
private IEnumerator WaitForRerollComplete()
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <WaitForRerollComplete>d__16(0)
{
<>4__this = this
};
}
private void OnDestroy()
{
if (_harmony != null)
{
_harmony.UnpatchSelf();
}
}
}
[HarmonyPatch(typeof(RunManager), "SetRunLevel")]
internal static class RunManager_SetRunLevel_Patch
{
private static bool Prefix(RunManager __instance)
{
if (!RejoinManager.RerollQueued || RejoinManager.IsShopReroll)
{
return true;
}
Plugin.Log.LogInfo((object)$"[HARMONY] Overriding SetRunLevel with seed: {RejoinManager.QueuedSeed}");
FieldInfo field = typeof(RunManager).GetField("levels", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
FieldInfo field2 = typeof(RunManager).GetField("levelCurrent", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (field == null || field2 == null)
{
Plugin.Log.LogError((object)"Could not find required fields!");
return true;
}
if (!(field.GetValue(__instance) is IList list) || list.Count == 0)
{
Plugin.Log.LogError((object)"Levels list is null or empty!");
return true;
}
Random.InitState(RejoinManager.QueuedSeed);
RejoinManager.SeedApplied = true;
int num = Random.Range(0, list.Count);
object obj = list[num];
field2.SetValue(__instance, obj);
object obj2 = ((obj is Object) ? obj : null);
string arg = ((obj2 != null) ? ((Object)obj2).name : null) ?? "unknown";
Plugin.Log.LogInfo((object)$"[HARMONY] Selected level [{num}]: {arg}");
RejoinManager.RerollQueued = false;
return false;
}
}
[HarmonyPatch(typeof(LevelGenerator), "Generate")]
internal static class LevelGenerator_Generate_Patch
{
private static void Prefix()
{
if (RejoinManager.RerollQueued && !RejoinManager.SeedApplied)
{
Plugin.Log.LogInfo((object)$"[HARMONY] Seeding RNG before Generate: {RejoinManager.QueuedSeed}");
Random.InitState(RejoinManager.QueuedSeed);
RejoinManager.SeedApplied = true;
if (!RejoinManager.IsShopReroll)
{
RejoinManager.RerollQueued = false;
}
}
}
}
[HarmonyPatch(typeof(ShopManager), "ShopInitialize")]
internal static class ShopManager_ShopInitialize_Patch
{
private static void Prefix()
{
if (RejoinManager.RerollQueued && RejoinManager.IsShopReroll && !RejoinManager.SeedApplied)
{
Plugin.Log.LogInfo((object)$"[HARMONY] Seeding RNG before ShopInitialize: {RejoinManager.QueuedSeed}");
Random.InitState(RejoinManager.QueuedSeed);
RejoinManager.SeedApplied = true;
}
if (RejoinManager.IsShopReroll)
{
RejoinManager.RerollQueued = false;
RejoinManager.IsShopReroll = false;
}
}
}
[HarmonyPatch(typeof(LevelGenerator), "GenerateDone")]
internal static class LevelGenerator_GenerateDone_Patch
{
[CompilerGenerated]
private sealed class <SaveHealthDelayed>d__1 : IEnumerator<object>, IDisposable, IEnumerator
{
private int <>1__state;
private object <>2__current;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <SaveHealthDelayed>d__1(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<>1__state = -2;
}
private bool MoveNext()
{
//IL_0026: Unknown result type (might be due to invalid IL or missing references)
//IL_0030: Expected O, but got Unknown
switch (<>1__state)
{
default:
return false;
case 0:
<>1__state = -1;
<>2__current = (object)new WaitForSeconds(1.5f);
<>1__state = 1;
return true;
case 1:
<>1__state = -1;
RejoinManager.SaveLevelStartHealth();
return false;
}
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
private static void Postfix()
{
Plugin.Log.LogInfo((object)"[HARMONY] GenerateDone called, saving level start health...");
if ((Object)(object)LevelGenerator.Instance != (Object)null)
{
((MonoBehaviour)LevelGenerator.Instance).StartCoroutine(SaveHealthDelayed());
}
}
[IteratorStateMachine(typeof(<SaveHealthDelayed>d__1))]
private static IEnumerator SaveHealthDelayed()
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <SaveHealthDelayed>d__1(0);
}
}