Decompiled source of QuickReroll v3.0.3

QuotaReroll.dll

Decompiled 2 days ago
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);
	}
}