Decompiled source of RunSaves v1.2.2

plugins/RunSaves.dll

Decompiled 2 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Core.Logging.Interpolation;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using HarmonyLib;
using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSystem;
using Il2CppSystem.Collections.Generic;
using Il2CppSystem.Reflection;
using Microsoft.CodeAnalysis;
using TMPro;
using Unity.Collections;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("RunSaves")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.2.2.0")]
[assembly: AssemblyInformationalVersion("1.2.2+2af9320131467f2bae516e4261b13e5fe2047bc9")]
[assembly: AssemblyProduct("RunSaves")]
[assembly: AssemblyTitle("RunSaves")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.2.2.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace RunSaves
{
	internal static class Game
	{
		private static RunManager run;

		private static PartyLevelManager party;

		private static ReviveManager revives;

		private static ImprovementsManager improvements;

		private static StoreManager store;

		internal static RunManager Run => Find<RunManager>(ref run);

		internal static PartyLevelManager Party => Find<PartyLevelManager>(ref party);

		internal static ReviveManager Revives => Find<ReviveManager>(ref revives);

		internal static ImprovementsManager Improvements => Find<ImprovementsManager>(ref improvements);

		internal static StoreManager Store => Find<StoreManager>(ref store);

		internal static bool StoreBusy
		{
			get
			{
				try
				{
					StoreManager val = Store;
					if ((Object)(object)val == (Object)null)
					{
						return false;
					}
					return val.showStoresCoroutine != null || val.IsShopPanelActive || val.IsExitingStore;
				}
				catch
				{
					return false;
				}
			}
		}

		internal static bool IsServer
		{
			get
			{
				try
				{
					NetworkManager singleton = NetworkManager.Singleton;
					return (Object)(object)singleton != (Object)null && singleton.IsListening && singleton.IsServer;
				}
				catch
				{
					return false;
				}
			}
		}

		internal static bool IsClientOnly
		{
			get
			{
				try
				{
					NetworkManager singleton = NetworkManager.Singleton;
					return (Object)(object)singleton != (Object)null && singleton.IsListening && !singleton.IsServer;
				}
				catch
				{
					return false;
				}
			}
		}

		private static T Find<T>(ref T cache) where T : Object
		{
			if (cache != null && !((Il2CppObjectBase)cache).WasCollected && (Object)(object)cache != (Object)null)
			{
				return cache;
			}
			cache = Object.FindObjectOfType<T>();
			return cache;
		}

		internal static void ForgetObjects()
		{
			run = null;
			party = null;
			revives = null;
			improvements = null;
			store = null;
		}

		internal static List<GameplayPlayerIdentity> Players()
		{
			List<GameplayPlayerIdentity> list = new List<GameplayPlayerIdentity>();
			foreach (GameplayPlayerIdentity item in Object.FindObjectsOfType<GameplayPlayerIdentity>())
			{
				if ((Object)(object)item != (Object)null && ((NetworkBehaviour)item).IsSpawned)
				{
					list.Add(item);
				}
			}
			list.Sort((GameplayPlayerIdentity a, GameplayPlayerIdentity b) => ((NetworkBehaviour)a).OwnerClientId.CompareTo(((NetworkBehaviour)b).OwnerClientId));
			return list;
		}

		internal static GameplayPlayerIdentity PlayerFor(ulong clientId)
		{
			foreach (GameplayPlayerIdentity item in Object.FindObjectsOfType<GameplayPlayerIdentity>())
			{
				if ((Object)(object)item != (Object)null && ((NetworkBehaviour)item).IsSpawned && ((NetworkBehaviour)item).OwnerClientId == clientId)
				{
					return item;
				}
			}
			return null;
		}

		internal static GameplayPlayerIdentity LocalPlayer()
		{
			foreach (GameplayPlayerIdentity item in Object.FindObjectsOfType<GameplayPlayerIdentity>())
			{
				if ((Object)(object)item != (Object)null && ((NetworkBehaviour)item).IsSpawned && ((NetworkBehaviour)item).IsOwner)
				{
					return item;
				}
			}
			return null;
		}

		internal static string KeyOf(GameplayPlayerIdentity p)
		{
			try
			{
				string platformPlayerId = p.PlatformPlayerId;
				if (!string.IsNullOrWhiteSpace(platformPlayerId))
				{
					return platformPlayerId;
				}
			}
			catch
			{
			}
			return "name:" + NameOf(p);
		}

		internal static string NameOf(GameplayPlayerIdentity p)
		{
			try
			{
				string displayName = p.GetDisplayName();
				if (!string.IsNullOrWhiteSpace(displayName))
				{
					return displayName;
				}
			}
			catch
			{
			}
			return $"Player {((NetworkBehaviour)p).OwnerClientId}";
		}

		internal static string CharacterOf(GameplayPlayerIdentity p)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				CharacterResource character = p.Character;
				if ((Object)(object)character != (Object)null)
				{
					return ((object)character.Id/*cast due to .constrained prefix*/).ToString();
				}
			}
			catch
			{
			}
			return "";
		}

		internal static List<SpellType> SpellsOf(GameplayPlayerIdentity p)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			List<SpellType> list = new List<SpellType>();
			try
			{
				SpellEquipper spellEquipper = p.spellEquipper;
				NetworkList<SpellTypeNetworkWrapper> val = ((spellEquipper != null) ? spellEquipper.equippedSpells : null);
				if (val != null)
				{
					for (int i = 0; i < val.Count; i++)
					{
						list.Add(val[i].SpellType);
					}
				}
			}
			catch
			{
			}
			return list;
		}

		internal static string WorldName(RunManager rm)
		{
			try
			{
				RunConfiguration configuration = rm.Configuration;
				if ((Object)(object)configuration == (Object)null)
				{
					return "Unknown world";
				}
				if ((Object)(object)configuration == (Object)(object)rm.verdantMeadowsConfiguration)
				{
					return "Verdant Meadows";
				}
				if ((Object)(object)configuration == (Object)(object)rm.frozenAncientsConfiguration)
				{
					return "Frozen Ancients";
				}
				if ((Object)(object)configuration == (Object)(object)rm.pyrestormPitConfiguration)
				{
					return "Pyrestorm Pit";
				}
				if ((Object)(object)configuration == (Object)(object)rm.astralPlanesConfiguration)
				{
					return "Astral Planes";
				}
				return ((Object)configuration).name;
			}
			catch
			{
				return "Unknown world";
			}
		}

		internal static string ConfigName(RunManager rm)
		{
			try
			{
				RunConfiguration configuration = rm.Configuration;
				return ((configuration != null) ? ((Object)configuration).name : null) ?? "";
			}
			catch
			{
				return "";
			}
		}

		internal static bool InMainMenu()
		{
			try
			{
				return (Object)(object)Object.FindObjectOfType<MainMenuManager>() != (Object)null && (Object)(object)Run == (Object)null;
			}
			catch
			{
				return false;
			}
		}

		internal static bool TryParse<T>(string s, out T value) where T : struct, Enum
		{
			return Enum.TryParse<T>(s, out value);
		}

		internal static PlayerArtifacts ArtifactsOf(GameplayPlayerIdentity p)
		{
			try
			{
				IPlayerArtifacts artifacts = p.Artifacts;
				return (artifacts != null) ? ((Il2CppObjectBase)artifacts).TryCast<PlayerArtifacts>() : null;
			}
			catch
			{
				return null;
			}
		}

		internal static Dictionary<string, float> ArtifactStacksOf(GameplayPlayerIdentity p)
		{
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Expected O, but got Unknown
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			Dictionary<string, float> dictionary = new Dictionary<string, float>();
			try
			{
				PlayerArtifacts obj = ArtifactsOf(p);
				List<IArtifactValue> val = ((obj != null) ? obj.artifactValues : null);
				if (val == null)
				{
					return dictionary;
				}
				for (int i = 0; i < val.Count; i++)
				{
					IArtifactValue val2 = val[i];
					if (val2 != null)
					{
						float stacks = val2.Stacks;
						if (stacks != 0f)
						{
							dictionary[((object)val2.ArtifactType/*cast due to .constrained prefix*/).ToString()] = stacks;
						}
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				bool flag = default(bool);
				BepInExWarningLogInterpolatedStringHandler val3 = new BepInExWarningLogInterpolatedStringHandler(25, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("Reading artifact stacks: ");
					((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<string>(ex.Message);
				}
				log.LogWarning(val3);
			}
			return dictionary;
		}

		internal unsafe static int SetArtifactStacks(GameplayPlayerIdentity p, Dictionary<string, float> saved)
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			int num = 0;
			if (saved == null || saved.Count == 0)
			{
				return 0;
			}
			PlayerArtifacts val = ArtifactsOf(p);
			List<IArtifactValue> val2 = ((val != null) ? val.artifactValues : null);
			if (val2 == null)
			{
				return 0;
			}
			for (int i = 0; i < val2.Count; i++)
			{
				IArtifactValue val3 = val2[i];
				if (val3 == null || !saved.TryGetValue(((object)val3.ArtifactType/*cast due to .constrained prefix*/).ToString(), out var value))
				{
					continue;
				}
				IntPtr pointer = ((Il2CppObjectBase)val3).Pointer;
				IntPtr intPtr = IntPtr.Zero;
				IntPtr intPtr2 = IL2CPP.il2cpp_object_get_class(pointer);
				while (intPtr2 != IntPtr.Zero && intPtr == IntPtr.Zero)
				{
					intPtr = IL2CPP.il2cpp_class_get_field_from_name(intPtr2, "<Stacks>k__BackingField");
					intPtr2 = IL2CPP.il2cpp_class_get_parent(intPtr2);
				}
				if (!(intPtr == IntPtr.Zero))
				{
					int num2 = (int)IL2CPP.il2cpp_field_get_offset(intPtr);
					*(float*)((byte*)(void*)pointer + num2) = value;
					try
					{
						val.NotifyStackChanged(val3.ArtifactType, value);
					}
					catch
					{
					}
					num++;
				}
			}
			return num;
		}

		internal static int RevivesPerformedOf(GameplayPlayerIdentity p)
		{
			try
			{
				return p.RevivesPerformed;
			}
			catch
			{
				return 0;
			}
		}
	}
	[HarmonyPatch]
	internal static class Net
	{
		private const string SpellsMsg = "girthy_mods.RunSaves.Spells";

		private const string TimeMsg = "girthy_mods.RunSaves.Time";

		private const string CountersMsg = "girthy_mods.RunSaves.Counters";

		private static HandleNamedMessageDelegate spellsHandler;

		private static HandleNamedMessageDelegate timeHandler;

		private static HandleNamedMessageDelegate countersHandler;

		internal static List<SpellType> PendingSpells;

		internal static float PendingUntil;

		[HarmonyPatch(typeof(RunManager), "OnNetworkSpawn")]
		[HarmonyPostfix]
		private static void Listen()
		{
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Expected O, but got Unknown
			try
			{
				NetworkManager singleton = NetworkManager.Singleton;
				if (!((Object)(object)singleton == (Object)null) && !singleton.IsServer && singleton.CustomMessagingManager != null)
				{
					if (spellsHandler == null)
					{
						spellsHandler = DelegateSupport.ConvertDelegate<HandleNamedMessageDelegate>((Delegate)new Action<ulong, FastBufferReader>(OnSpells));
					}
					if (timeHandler == null)
					{
						timeHandler = DelegateSupport.ConvertDelegate<HandleNamedMessageDelegate>((Delegate)new Action<ulong, FastBufferReader>(OnTime));
					}
					singleton.CustomMessagingManager.RegisterNamedMessageHandler("girthy_mods.RunSaves.Spells", spellsHandler);
					singleton.CustomMessagingManager.RegisterNamedMessageHandler("girthy_mods.RunSaves.Time", timeHandler);
					if (countersHandler == null)
					{
						countersHandler = DelegateSupport.ConvertDelegate<HandleNamedMessageDelegate>((Delegate)new Action<ulong, FastBufferReader>(OnCounters));
					}
					singleton.CustomMessagingManager.RegisterNamedMessageHandler("girthy_mods.RunSaves.Counters", countersHandler);
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(8, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Listen: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Exception>(ex);
				}
				log.LogError(val);
			}
		}

		private static void Send(string name, string payload, ulong? client)
		{
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			NetworkManager singleton = NetworkManager.Singleton;
			if ((Object)(object)singleton == (Object)null || !singleton.IsServer || singleton.CustomMessagingManager == null)
			{
				return;
			}
			FastBufferWriter val = default(FastBufferWriter);
			((FastBufferWriter)(ref val))..ctor(Encoding.UTF8.GetByteCount(payload) * 2 + 64, (Allocator)2, -1);
			try
			{
				((FastBufferWriter)(ref val)).WriteValueSafe(payload, false);
				if (client.HasValue)
				{
					singleton.CustomMessagingManager.SendNamedMessage(name, client.Value, val, (NetworkDelivery)4);
				}
				else
				{
					singleton.CustomMessagingManager.SendNamedMessageToAll(name, val, (NetworkDelivery)4);
				}
			}
			finally
			{
				((FastBufferWriter)(ref val)).Dispose();
			}
		}

		internal unsafe static void SendSpells(ulong client, List<SpellType> spells)
		{
			Send("girthy_mods.RunSaves.Spells", string.Join(",", spells.ConvertAll((SpellType s) => ((object)(*(SpellType*)(&s))/*cast due to .constrained prefix*/).ToString())), client);
		}

		internal static void SendTime(float cycleTime, float runTime)
		{
			Send("girthy_mods.RunSaves.Time", cycleTime.ToString(CultureInfo.InvariantCulture) + ";" + runTime.ToString(CultureInfo.InvariantCulture), null);
		}

		internal static void SendCounters(ulong client, Dictionary<string, float> stacks, int revivesPerformed)
		{
			List<string> list = new List<string>();
			if (stacks != null)
			{
				foreach (KeyValuePair<string, float> stack in stacks)
				{
					list.Add(stack.Key + "=" + stack.Value.ToString(CultureInfo.InvariantCulture));
				}
			}
			if (list.Count != 0 || revivesPerformed != 0)
			{
				Send("girthy_mods.RunSaves.Counters", client.ToString(CultureInfo.InvariantCulture) + "|" + revivesPerformed.ToString(CultureInfo.InvariantCulture) + "|" + string.Join(",", list), null);
			}
		}

		private static void OnCounters(ulong sender, FastBufferReader reader)
		{
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Expected O, but got Unknown
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Expected O, but got Unknown
			bool flag = default(bool);
			try
			{
				string text = default(string);
				((FastBufferReader)(ref reader)).ReadValueSafe(ref text, false);
				string[] array = text.Split('|');
				if (array.Length < 3)
				{
					return;
				}
				ulong clientId = ulong.Parse(array[0], CultureInfo.InvariantCulture);
				int revivesPerformed = int.Parse(array[1], CultureInfo.InvariantCulture);
				Dictionary<string, float> dictionary = new Dictionary<string, float>();
				string[] array2 = array[2].Split(',', StringSplitOptions.RemoveEmptyEntries);
				foreach (string text2 in array2)
				{
					int num = text2.IndexOf('=');
					if (num > 0)
					{
						dictionary[text2.Substring(0, num)] = float.Parse(text2.Substring(num + 1), CultureInfo.InvariantCulture);
					}
				}
				GameplayPlayerIdentity val = Game.PlayerFor(clientId);
				if (!((Object)(object)val == (Object)null))
				{
					int num2 = Restorer.ApplyCounters(val, dictionary, revivesPerformed);
					ManualLogSource log = Plugin.Log;
					BepInExInfoLogInterpolatedStringHandler val2 = new BepInExInfoLogInterpolatedStringHandler(41, 2, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Host restored run: ");
						((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(num2);
						((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" artifact stacks for ");
						((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(Game.NameOf(val));
						((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(".");
					}
					log.LogInfo(val2);
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log2 = Plugin.Log;
				BepInExErrorLogInterpolatedStringHandler val3 = new BepInExErrorLogInterpolatedStringHandler(18, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("Receive counters: ");
					((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<Exception>(ex);
				}
				log2.LogError(val3);
			}
		}

		private static void OnSpells(ulong sender, FastBufferReader reader)
		{
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Expected O, but got Unknown
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Expected O, but got Unknown
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			bool flag = default(bool);
			try
			{
				string text = default(string);
				((FastBufferReader)(ref reader)).ReadValueSafe(ref text, false);
				List<SpellType> list = new List<SpellType>();
				string[] array = text.Split(',', StringSplitOptions.RemoveEmptyEntries);
				for (int i = 0; i < array.Length; i++)
				{
					if (Enum.TryParse<SpellType>(array[i], out SpellType result))
					{
						list.Add(result);
					}
				}
				PendingSpells = list;
				PendingUntil = Time.unscaledTime + 15f;
				ManualLogSource log = Plugin.Log;
				BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(33, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Host restored run: re-equipping ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(text);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(".");
				}
				log.LogInfo(val);
				TryEquipPending();
			}
			catch (Exception ex)
			{
				ManualLogSource log2 = Plugin.Log;
				BepInExErrorLogInterpolatedStringHandler val2 = new BepInExErrorLogInterpolatedStringHandler(16, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Receive spells: ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<Exception>(ex);
				}
				log2.LogError(val2);
			}
		}

		internal static void TryEquipPending()
		{
			if (PendingSpells == null)
			{
				return;
			}
			if (Time.unscaledTime > PendingUntil)
			{
				Plugin.Log.LogWarning((object)"Couldn't re-equip restored spells: your wizard wasn't ready in time.");
				PendingSpells = null;
				return;
			}
			GameplayPlayerIdentity val = Game.LocalPlayer();
			if (!((Object)(object)((val != null) ? val.spellEquipper : null) == (Object)null) && Game.SpellsOf(val).Count != 0)
			{
				EquipMissing(val, PendingSpells);
				PendingSpells = null;
			}
		}

		internal static int EquipMissing(GameplayPlayerIdentity p, List<SpellType> spells)
		{
			//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_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			List<SpellType> list = Game.SpellsOf(p);
			int num = 0;
			foreach (SpellType spell in spells)
			{
				if (!list.Contains(spell))
				{
					p.spellEquipper.EquipSpell(spell);
					list.Add(spell);
					num++;
				}
			}
			return num;
		}

		private static void OnTime(ulong sender, FastBufferReader reader)
		{
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Expected O, but got Unknown
			try
			{
				string text = default(string);
				((FastBufferReader)(ref reader)).ReadValueSafe(ref text, false);
				string[] array = text.Split(';');
				RunManager run = Game.Run;
				if ((Object)(object)run == (Object)null || array.Length < 2)
				{
					return;
				}
				float num = (run.currentCycleTimeInSeconds = float.Parse(array[0], CultureInfo.InvariantCulture));
				run.currentRunTimeInSeconds = float.Parse(array[1], CultureInfo.InvariantCulture);
				List<EliteEnemyConfiguration> eliteEnemiesThatHaveBeenSpawned = run.eliteEnemiesThatHaveBeenSpawned;
				RunConfiguration configuration = run.Configuration;
				List<EliteEnemyConfiguration> val = ((configuration != null) ? configuration.EliteEnemies : null);
				if (eliteEnemiesThatHaveBeenSpawned == null || val == null)
				{
					return;
				}
				for (int i = 0; i < val.Count; i++)
				{
					EliteEnemyConfiguration val2 = val[i];
					if ((Object)(object)val2 != (Object)null && (float)val2.SpawnTimeInSeconds() < num && !eliteEnemiesThatHaveBeenSpawned.Contains(val2))
					{
						eliteEnemiesThatHaveBeenSpawned.Add(val2);
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val3 = new BepInExErrorLogInterpolatedStringHandler(14, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("Receive time: ");
					((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<Exception>(ex);
				}
				log.LogError(val3);
			}
		}
	}
	[BepInPlugin("girthy_mods.RunSaves", "RunSaves", "1.2.2")]
	public class Plugin : BasePlugin
	{
		public const string Guid = "girthy_mods.RunSaves";

		public const string Name = "RunSaves";

		public const string Version = "1.2.2";

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<bool> AutosaveEachWave;

		internal static ConfigEntry<int> AutosaveIntervalSeconds;

		internal static ConfigEntry<bool> SaveOnQuit;

		internal static ConfigEntry<bool> ToastOnAutosave;

		internal static ConfigEntry<bool> ShowSaveButton;

		internal static ConfigEntry<string> SaveKey;

		internal static ConfigEntry<bool> ShowLobbyPrompt;

		internal static ConfigEntry<string> LobbyPanelSide;

		internal static ConfigEntry<string> PanelKey;

		public override void Load()
		{
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Expected O, but got Unknown
			//IL_015f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Expected O, but got Unknown
			//IL_0197: Unknown result type (might be due to invalid IL or missing references)
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0201: Expected O, but got Unknown
			Log = ((BasePlugin)this).Log;
			Enabled = ((BasePlugin)this).Config.Bind<bool>("General", "Enabled", true, "Turn run saving and restoring on or off.");
			AutosaveEachWave = ((BasePlugin)this).Config.Bind<bool>("Saving", "AutosaveEachWave", true, "Host: save automatically when each new wave starts, in every run, including restored ones (the previous save is kept as save.prev.json).");
			AutosaveIntervalSeconds = ((BasePlugin)this).Config.Bind<int>("Saving", "AutosaveIntervalSeconds", 60, new ConfigDescription("Host: also save quietly every this many seconds (0 = off). Keeps upgrades and levels gained mid-wave; a restore still starts at the beginning of the saved wave.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 600), Array.Empty<object>()));
			SaveOnQuit = ((BasePlugin)this).Config.Bind<bool>("Saving", "SaveOnQuit", true, "Host: save when quitting the run from the pause menu.");
			ToastOnAutosave = ((BasePlugin)this).Config.Bind<bool>("Saving", "ShowAutosaveMessage", true, "Show a short message when an autosave happens.");
			ShowSaveButton = ((BasePlugin)this).Config.Bind<bool>("Saving", "PauseMenuButton", true, "Host: add a Save Run button to the pause menu.");
			SaveKey = ((BasePlugin)this).Config.Bind<string>("Saving", "SaveKey", "", "Optional key to save during a run (Unity Input System key name, e.g. F5). Empty = no key.");
			ShowLobbyPrompt = ((BasePlugin)this).Config.Bind<bool>("Restoring", "LobbyPrompt", true, "Host: show the saved run in the lobby with Resume / Delete / Hide.");
			LobbyPanelSide = ((BasePlugin)this).Config.Bind<string>("Restoring", "LobbyPanelSide", "Right", new ConfigDescription("Which side of the lobby the saved-run panel sits on.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[2] { "Right", "Left" }), Array.Empty<object>()));
			PanelKey = ((BasePlugin)this).Config.Bind<string>("Restoring", "PanelKey", "F10", "Key to show or hide the saved-run panel in the lobby (Unity Input System key name). Hide also leaves a small \"Saved Run\" tab to click.");
			Harmony val = new Harmony("girthy_mods.RunSaves");
			val.PatchAll(typeof(Recorder));
			val.PatchAll(typeof(Saver));
			val.PatchAll(typeof(Restorer));
			val.PatchAll(typeof(Net));
			val.PatchAll(typeof(PauseButton));
			((BasePlugin)this).AddComponent<RunSavesUI>();
			ManualLogSource log = Log;
			bool flag = default(bool);
			BepInExInfoLogInterpolatedStringHandler val2 = new BepInExInfoLogInterpolatedStringHandler(8, 2, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>("RunSaves");
				((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" ");
				((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>("1.2.2");
				((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" ready.");
			}
			log.LogInfo(val2);
		}
	}
	[HarmonyPatch]
	internal static class Recorder
	{
		internal static readonly Dictionary<string, List<Improvement>> Log = new Dictionary<string, List<Improvement>>();

		internal static readonly Dictionary<string, SavedPlayer> Known = new Dictionary<string, SavedPlayer>();

		private static readonly Dictionary<ulong, float> initTime = new Dictionary<ulong, float>();

		private static readonly Dictionary<ulong, string> keyCache = new Dictionary<ulong, string>();

		internal static bool Suppress;

		internal static bool RunActive;

		private const float StartingStatsWindow = 10f;

		internal static void ResetForNewRun()
		{
			Log.Clear();
			Known.Clear();
			initTime.Clear();
			keyCache.Clear();
			RunActive = false;
		}

		private static bool Executing(NetworkBehaviour b)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Invalid comparison between Unknown and I4
			try
			{
				return (int)b.__rpc_exec_stage == 1;
			}
			catch
			{
				return false;
			}
		}

		private static bool ShouldRecord(NetworkBehaviour b)
		{
			if (Plugin.Enabled.Value && !Suppress && RunActive && Game.IsServer)
			{
				return Executing(b);
			}
			return false;
		}

		internal static string KeyFor(ulong clientId)
		{
			if (keyCache.TryGetValue(clientId, out var value))
			{
				return value;
			}
			GameplayPlayerIdentity val = Game.PlayerFor(clientId);
			if ((Object)(object)val == (Object)null)
			{
				return $"client:{clientId}";
			}
			value = Game.KeyOf(val);
			keyCache[clientId] = value;
			Remember(val);
			return value;
		}

		internal unsafe static void Remember(GameplayPlayerIdentity p)
		{
			try
			{
				string key = Game.KeyOf(p);
				if (!Known.TryGetValue(key, out var value))
				{
					Dictionary<string, SavedPlayer> known = Known;
					SavedPlayer obj = new SavedPlayer
					{
						Key = key
					};
					value = obj;
					known[key] = obj;
				}
				value.Name = Game.NameOf(p);
				value.Character = Game.CharacterOf(p);
				value.WasHost = ((NetworkBehaviour)p).IsOwner;
				List<SpellType> list = Game.SpellsOf(p);
				if (list.Count > 0)
				{
					value.Spells = list.ConvertAll((SpellType s) => ((object)(*(SpellType*)(&s))/*cast due to .constrained prefix*/).ToString());
				}
				value.ArtifactStacks = Game.ArtifactStacksOf(p);
				value.RevivesPerformed = Game.RevivesPerformedOf(p);
			}
			catch
			{
			}
		}

		internal static void Add(string key, Improvement imp)
		{
			if (!Log.TryGetValue(key, out var value))
			{
				value = (Log[key] = new List<Improvement>());
			}
			value.Add(imp);
		}

		[HarmonyPatch(typeof(RunManager), "OnNetworkSpawn")]
		[HarmonyPostfix]
		private static void RunSpawned()
		{
			Game.ForgetObjects();
			ResetForNewRun();
		}

		[HarmonyPatch(typeof(RunManager), "OnDestroy")]
		[HarmonyPostfix]
		private static void RunDespawned()
		{
			RunActive = false;
			Restorer.Abort("the run ended");
			Game.ForgetObjects();
		}

		[HarmonyPatch(typeof(GameplayPlayerIdentity), "InitializeOnClientsRpc")]
		[HarmonyPrefix]
		private static void PlayerInitialized(GameplayPlayerIdentity __instance)
		{
			try
			{
				if (Game.IsServer && Executing((NetworkBehaviour)(object)__instance))
				{
					initTime[((NetworkBehaviour)__instance).OwnerClientId] = Time.unscaledTime;
					keyCache.Remove(((NetworkBehaviour)__instance).OwnerClientId);
				}
			}
			catch
			{
			}
		}

		[HarmonyPatch(typeof(ImprovementsManager), "SendCharacterStatModifiersRpc")]
		[HarmonyPrefix]
		private static void Stats(ImprovementsManager __instance, ulong clientId, Il2CppReferenceArray<StatModifierMessage> messages)
		{
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Expected O, but got Unknown
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: 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_007c: Unknown result type (might be due to invalid IL or missing references)
			if (!ShouldRecord((NetworkBehaviour)(object)__instance) || messages == null)
			{
				return;
			}
			try
			{
				if (initTime.TryGetValue(clientId, out var value) && Time.unscaledTime - value < 10f)
				{
					return;
				}
				List<StatEntry> list = new List<StatEntry>();
				foreach (StatModifierMessage item in (Il2CppArrayBase<StatModifierMessage>)(object)messages)
				{
					if (item != null)
					{
						list.Add(new StatEntry
						{
							Stat = ((object)item.Stat/*cast due to .constrained prefix*/).ToString(),
							Value = item.Value,
							Type = ((object)item.Type/*cast due to .constrained prefix*/).ToString()
						});
					}
				}
				if (list.Count > 0)
				{
					Add(KeyFor(clientId), new Improvement
					{
						Kind = "Stats",
						Stats = list
					});
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(14, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Record stats: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Exception>(ex);
				}
				log.LogError(val);
			}
		}

		[HarmonyPatch(typeof(ImprovementsManager), "SendSpellStatModifierRpc")]
		[HarmonyPrefix]
		private static void SpellStat(ImprovementsManager __instance, ulong clientId, SpellStatModifierMessage message)
		{
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Expected O, but got Unknown
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			if (!ShouldRecord((NetworkBehaviour)(object)__instance) || message == null)
			{
				return;
			}
			try
			{
				Add(KeyFor(clientId), new Improvement
				{
					Kind = "SpellStat",
					Spell = ((object)message.Spell/*cast due to .constrained prefix*/).ToString(),
					Stat = ((object)message.Stat/*cast due to .constrained prefix*/).ToString(),
					Value = message.Value
				});
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(22, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Record spell upgrade: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Exception>(ex);
				}
				log.LogError(val);
			}
		}

		[HarmonyPatch(typeof(ImprovementsManager), "SendSpellInfusionRpc")]
		[HarmonyPrefix]
		private static void Infusion(ImprovementsManager __instance, ulong clientId, SpellInfusionMessage message)
		{
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Expected O, but got Unknown
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			if (!ShouldRecord((NetworkBehaviour)(object)__instance) || message == null)
			{
				return;
			}
			try
			{
				Add(KeyFor(clientId), new Improvement
				{
					Kind = "Infusion",
					Spell = ((object)message.Spell/*cast due to .constrained prefix*/).ToString(),
					Element = ((object)message.Element/*cast due to .constrained prefix*/).ToString()
				});
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(17, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Record infusion: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Exception>(ex);
				}
				log.LogError(val);
			}
		}

		[HarmonyPatch(typeof(ImprovementsManager), "SendSpellVariantRpc")]
		[HarmonyPrefix]
		private static void Variant(ImprovementsManager __instance, ulong clientId, SpellVariantMessage message)
		{
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Expected O, but got Unknown
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			if (!ShouldRecord((NetworkBehaviour)(object)__instance) || message == null)
			{
				return;
			}
			try
			{
				Add(KeyFor(clientId), new Improvement
				{
					Kind = "Variant",
					Spell = ((object)message.Spell/*cast due to .constrained prefix*/).ToString(),
					Variant = ((object)message.SpellVariant/*cast due to .constrained prefix*/).ToString()
				});
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(22, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Record spell variant: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Exception>(ex);
				}
				log.LogError(val);
			}
		}

		[HarmonyPatch(typeof(ImprovementsManager), "SendArtifactRpc")]
		[HarmonyPrefix]
		private static void Artifact(ImprovementsManager __instance, ulong clientId, ArtifactMessage message)
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Expected O, but got Unknown
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			if (!ShouldRecord((NetworkBehaviour)(object)__instance) || message == null)
			{
				return;
			}
			try
			{
				Add(KeyFor(clientId), new Improvement
				{
					Kind = "Artifact",
					Artifact = ((object)message.Artifact/*cast due to .constrained prefix*/).ToString()
				});
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(17, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Record artifact: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Exception>(ex);
				}
				log.LogError(val);
			}
		}

		[HarmonyPatch(typeof(ImprovementsManager), "SendFinalUpgradeRpc")]
		[HarmonyPrefix]
		private unsafe static void Final(ImprovementsManager __instance, ulong clientId, FinalUpgradeType finalUpgradeType)
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Expected O, but got Unknown
			if (!ShouldRecord((NetworkBehaviour)(object)__instance))
			{
				return;
			}
			try
			{
				Add(KeyFor(clientId), new Improvement
				{
					Kind = "Final",
					Final = ((object)(*(FinalUpgradeType*)(&finalUpgradeType))/*cast due to .constrained prefix*/).ToString()
				});
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(22, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Record final upgrade: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Exception>(ex);
				}
				log.LogError(val);
			}
		}
	}
	[HarmonyPatch]
	internal static class Restorer
	{
		private enum Phase
		{
			None,
			WaitPlayers,
			WaitSpells,
			Settle,
			Replay,
			Finish
		}

		private class Match
		{
			public GameplayPlayerIdentity Player;

			public SavedPlayer Saved;

			public List<SpellType> Spells = new List<SpellType>();
		}

		internal static RunSave Armed;

		private static RunSave active;

		private static Phase phase;

		private static float t0;

		private static float phaseStart;

		private static bool wavesJumped;

		private static bool startedThisRun;

		private static readonly List<Match> matches = new List<Match>();

		private static readonly Queue<(Match m, Improvement imp)> queue = new Queue<(Match, Improvement)>();

		private static int applied;

		private static int skipped;

		private static readonly List<string> notes = new List<string>();

		private static bool revivesUsedSinceStart;

		internal static bool Busy => active != null;

		internal static void Abort(string why)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Expected O, but got Unknown
			if (active != null)
			{
				ManualLogSource log = Plugin.Log;
				bool flag = default(bool);
				BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(18, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Restore stopped: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(why);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(".");
				}
				log.LogWarning(val);
				Recorder.Suppress = false;
				while (queue.Count > 0)
				{
					var (match, imp) = queue.Dequeue();
					Recorder.Add(match.Saved.Key, imp);
				}
				active = null;
				phase = Phase.None;
			}
		}

		[HarmonyPatch(typeof(RunManager), "OnNetworkSpawn")]
		[HarmonyPostfix]
		private static void RunSpawned()
		{
			startedThisRun = false;
			wavesJumped = false;
		}

		[HarmonyPatch(typeof(RunManager), "StartRunOnServerRpc")]
		[HarmonyPrefix]
		private static void StartRun_Prefix(RunManager __instance, out bool __state)
		{
			//IL_0217: Unknown result type (might be due to invalid IL or missing references)
			//IL_021e: Expected O, but got Unknown
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Invalid comparison between Unknown and I4
			//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a7: Expected O, but got Unknown
			__state = false;
			bool flag = default(bool);
			try
			{
				__state = (int)((NetworkBehaviour)__instance).__rpc_exec_stage == 1 && Game.IsServer;
				if (!__state || startedThisRun || Armed == null || !Plugin.Enabled.Value)
				{
					return;
				}
				RunSave armed = Armed;
				Armed = null;
				string text = Game.ConfigName(__instance);
				if (!string.IsNullOrEmpty(armed.ConfigName) && text != armed.ConfigName)
				{
					string text2 = $"Saved run is in {armed.World}, but this run is in {Game.WorldName(__instance)}. Not restored (the save is kept).";
					Plugin.Log.LogWarning((object)text2);
					Toast.Show(text2, 8f);
					return;
				}
				try
				{
					GameplayPlayerIdentity val = Game.LocalPlayer();
					if ((Object)(object)val != (Object)null && val.ActiveCurseLevel != armed.CurseLevel)
					{
						Toast.Show($"Heads up: the saved run used curse level {armed.CurseLevel}, this one uses {val.ActiveCurseLevel}.", 6f);
					}
				}
				catch
				{
				}
				active = armed;
				wavesJumped = false;
				if (armed.Cycle > 1 && __instance.CycleNumber != null && __instance.CycleNumber.Value < armed.Cycle)
				{
					__instance.CycleNumber.Value = armed.Cycle;
				}
				ManualLogSource log = Plugin.Log;
				BepInExInfoLogInterpolatedStringHandler val2 = new BepInExInfoLogInterpolatedStringHandler(37, 3, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Restoring saved run: ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(armed.World);
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(", cycle ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(armed.Cycle);
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(", wave ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(armed.Wave);
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(".");
				}
				log.LogInfo(val2);
			}
			catch (Exception ex)
			{
				ManualLogSource log2 = Plugin.Log;
				BepInExErrorLogInterpolatedStringHandler val3 = new BepInExErrorLogInterpolatedStringHandler(17, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("Restore (start): ");
					((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<Exception>(ex);
				}
				log2.LogError(val3);
				active = null;
			}
		}

		[HarmonyPatch(typeof(RunManager), "StartRunOnServerRpc")]
		[HarmonyPostfix]
		private static void StartRun_Postfix(RunManager __instance, bool __state)
		{
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Expected O, but got Unknown
			if (!__state || startedThisRun)
			{
				return;
			}
			startedThisRun = true;
			Recorder.RunActive = true;
			Saver.OnRunStarted(active != null);
			if (active == null)
			{
				return;
			}
			try
			{
				if (wavesJumped)
				{
					Net.SendTime(__instance.currentCycleTimeInSeconds, __instance.currentRunTimeInSeconds);
				}
				SetLevelAndRevives(active);
				t0 = Time.unscaledTime;
				SetPhase(Phase.WaitPlayers);
				Toast.Show($"Restoring saved run - wave {active.Wave}...", 4f);
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(23, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Restore (after start): ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Exception>(ex);
				}
				log.LogError(val);
			}
		}

		[HarmonyPatch(typeof(_StartWaves_d__53), "MoveNext")]
		[HarmonyPrefix]
		private static void Waves_Prefix(_StartWaves_d__53 __instance)
		{
			//IL_01d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e0: Expected O, but got Unknown
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			//IL_015c: Expected O, but got Unknown
			if (active == null || wavesJumped)
			{
				return;
			}
			bool flag = default(bool);
			try
			{
				if (__instance.__1__state != 0)
				{
					return;
				}
				RunManager _4__this = __instance.__4__this;
				RunConfiguration val = ((_4__this != null) ? _4__this.Configuration : null);
				List<WaveConfiguration> val2 = ((val != null) ? val.Waves : null);
				if (val2 == null || val2.Count == 0)
				{
					return;
				}
				wavesJumped = true;
				int count = val2.Count;
				int num = Math.Clamp(active.Wave, 1, count);
				float num2 = val.RunDurationInSeconds / (float)count;
				__instance._waves_5__2 = val2;
				__instance._timeBetweenWavesInSeconds_5__3 = num2;
				__instance._index_5__4 = num - 2;
				_4__this.CurrentWave.Value = num - 1;
				__instance.__1__state = 1;
				float num3 = (_4__this.currentCycleTimeInSeconds = (float)(num - 1) * num2);
				_4__this.currentRunTimeInSeconds = Math.Max(num3, active.RunTime - active.CycleTime + num3);
				int num5 = 0;
				List<EliteEnemyConfiguration> eliteEnemiesThatHaveBeenSpawned = _4__this.eliteEnemiesThatHaveBeenSpawned;
				List<EliteEnemyConfiguration> eliteEnemies = val.EliteEnemies;
				if (eliteEnemiesThatHaveBeenSpawned != null && eliteEnemies != null)
				{
					for (int i = 0; i < eliteEnemies.Count; i++)
					{
						EliteEnemyConfiguration val3 = eliteEnemies[i];
						if ((Object)(object)val3 != (Object)null && (float)val3.SpawnTimeInSeconds() < num3 && !eliteEnemiesThatHaveBeenSpawned.Contains(val3))
						{
							eliteEnemiesThatHaveBeenSpawned.Add(val3);
							num5++;
						}
					}
				}
				ManualLogSource log = Plugin.Log;
				BepInExInfoLogInterpolatedStringHandler val4 = new BepInExInfoLogInterpolatedStringHandler(70, 4, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val4).AppendLiteral("Restore: starting at wave ");
					((BepInExLogInterpolatedStringHandler)val4).AppendFormatted<int>(num);
					((BepInExLogInterpolatedStringHandler)val4).AppendLiteral("/");
					((BepInExLogInterpolatedStringHandler)val4).AppendFormatted<int>(count);
					((BepInExLogInterpolatedStringHandler)val4).AppendLiteral(" (");
					((BepInExLogInterpolatedStringHandler)val4).AppendFormatted<float>(num3, "0");
					((BepInExLogInterpolatedStringHandler)val4).AppendLiteral("s into the run), ");
					((BepInExLogInterpolatedStringHandler)val4).AppendFormatted<int>(num5);
					((BepInExLogInterpolatedStringHandler)val4).AppendLiteral(" earlier elites skipped.");
				}
				log.LogInfo(val4);
			}
			catch (Exception ex)
			{
				ManualLogSource log2 = Plugin.Log;
				BepInExErrorLogInterpolatedStringHandler val5 = new BepInExErrorLogInterpolatedStringHandler(17, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val5).AppendLiteral("Restore (waves): ");
					((BepInExLogInterpolatedStringHandler)val5).AppendFormatted<Exception>(ex);
				}
				log2.LogError(val5);
			}
		}

		private static void SetPhase(Phase p)
		{
			phase = p;
			phaseStart = Time.unscaledTime;
		}

		internal static void Tick()
		{
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_018f: Expected O, but got Unknown
			if (active == null || phase == Phase.None)
			{
				return;
			}
			try
			{
				if (!Game.IsServer || (Object)(object)Game.Run == (Object)null)
				{
					Abort("not hosting a run any more");
					return;
				}
				float unscaledTime = Time.unscaledTime;
				switch (phase)
				{
				case Phase.WaitPlayers:
				{
					List<GameplayPlayerIdentity> list = Game.Players();
					if ((list.Count > 0 && list.TrueForAll((GameplayPlayerIdentity p) => Game.SpellsOf(p).Count > 0) && unscaledTime - t0 >= 1.5f) || unscaledTime - t0 > 15f)
					{
						MatchPlayers(list);
						EquipSpells();
						SetPhase(Phase.WaitSpells);
					}
					break;
				}
				case Phase.WaitSpells:
					if ((matches.TrueForAll((Match m) => m.Spells.TrueForAll((SpellType s) => Game.SpellsOf(m.Player).Contains(s))) && unscaledTime - t0 >= 4f) || unscaledTime - phaseStart > 10f)
					{
						SetPhase(Phase.Settle);
					}
					break;
				case Phase.Settle:
					if (unscaledTime - phaseStart >= 1.5f)
					{
						BuildQueue();
						SetPhase(Phase.Replay);
					}
					break;
				case Phase.Replay:
				{
					for (int i = 0; i < 10; i++)
					{
						if (queue.Count <= 0)
						{
							break;
						}
						Apply(queue.Dequeue());
					}
					if (queue.Count == 0)
					{
						SetPhase(Phase.Finish);
					}
					break;
				}
				case Phase.Finish:
					FinishUp();
					break;
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(9, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Restore: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Exception>(ex);
				}
				log.LogError(val);
				Abort("an error (see above)");
			}
		}

		private static void MatchPlayers(List<GameplayPlayerIdentity> players)
		{
			//IL_02f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0306: Unknown result type (might be due to invalid IL or missing references)
			matches.Clear();
			notes.Clear();
			List<SavedPlayer> list = new List<SavedPlayer>(active.Players);
			List<GameplayPlayerIdentity> list2 = new List<GameplayPlayerIdentity>();
			foreach (GameplayPlayerIdentity player in players)
			{
				string key = Game.KeyOf(player);
				SavedPlayer savedPlayer = list.Find((SavedPlayer s) => s.Key == key);
				if (savedPlayer != null && !string.IsNullOrEmpty(savedPlayer.Character) && savedPlayer.Character != Game.CharacterOf(player))
				{
					notes.Add($"{Game.NameOf(player)} picked {RunSave.Pretty(Game.CharacterOf(player))} instead of {RunSave.Pretty(savedPlayer.Character)}");
					savedPlayer = null;
				}
				if (savedPlayer != null)
				{
					list.Remove(savedPlayer);
					matches.Add(new Match
					{
						Player = player,
						Saved = savedPlayer
					});
				}
				else
				{
					list2.Add(player);
				}
			}
			foreach (GameplayPlayerIdentity item in list2)
			{
				string ch = Game.CharacterOf(item);
				SavedPlayer savedPlayer2 = list.Find((SavedPlayer s) => s.Character == ch);
				if (savedPlayer2 != null)
				{
					list.Remove(savedPlayer2);
					matches.Add(new Match
					{
						Player = item,
						Saved = savedPlayer2
					});
					notes.Add(Game.NameOf(item) + " gets " + savedPlayer2.Name + "'s build (same wizard)");
				}
				else
				{
					notes.Add(Game.NameOf(item) + " isn't in the save (starts fresh)");
				}
			}
			foreach (SavedPlayer item2 in list)
			{
				notes.Add(item2.Name + " isn't here (build kept in the save)");
				if (!Recorder.Log.ContainsKey(item2.Key))
				{
					Recorder.Log[item2.Key] = new List<Improvement>(item2.Improvements);
				}
				if (!Recorder.Known.ContainsKey(item2.Key))
				{
					Recorder.Known[item2.Key] = item2;
				}
			}
			foreach (Match match in matches)
			{
				foreach (string spell in match.Saved.Spells)
				{
					if (Enum.TryParse<SpellType>(spell, out SpellType result) && !match.Spells.Contains(result))
					{
						match.Spells.Add(result);
					}
				}
			}
			foreach (string note in notes)
			{
				Plugin.Log.LogInfo((object)("Restore: " + note));
			}
		}

		private static void EquipSpells()
		{
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Expected O, but got Unknown
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Expected O, but got Unknown
			bool flag = default(bool);
			foreach (Match match in matches)
			{
				if (match.Spells.Count == 0)
				{
					continue;
				}
				if (((NetworkBehaviour)match.Player).IsOwner)
				{
					int num = Net.EquipMissing(match.Player, match.Spells);
					ManualLogSource log = Plugin.Log;
					BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(31, 2, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Restore: equipped ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(num);
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" spells for ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(Game.NameOf(match.Player));
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral(".");
					}
					log.LogInfo(val);
				}
				else
				{
					Net.SendSpells(((NetworkBehaviour)match.Player).OwnerClientId, match.Spells);
					ManualLogSource log2 = Plugin.Log;
					BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(36, 2, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Restore: asked ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(Game.NameOf(match.Player));
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" to re-equip ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(match.Spells.Count);
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" spells.");
					}
					log2.LogInfo(val);
				}
			}
		}

		private static void BuildQueue()
		{
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Expected O, but got Unknown
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			queue.Clear();
			applied = (skipped = 0);
			foreach (Match match in matches)
			{
				List<SpellType> list = Game.SpellsOf(match.Player);
				int num = 0;
				foreach (Improvement improvement in match.Saved.Improvements)
				{
					if (!string.IsNullOrEmpty(improvement.Spell) && (!Enum.TryParse<SpellType>(improvement.Spell, out SpellType result) || !list.Contains(result)))
					{
						num++;
						skipped++;
					}
					else
					{
						queue.Enqueue((match, improvement));
					}
				}
				if (num > 0)
				{
					notes.Add($"{Game.NameOf(match.Player)}: {num} spell upgrades skipped (spells not re-equipped - do they have RunSaves?)");
				}
			}
			ManualLogSource log = Plugin.Log;
			bool flag = default(bool);
			BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(29, 1, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Restore: replaying ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(queue.Count);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" upgrades.");
			}
			log.LogInfo(val);
		}

		private static void Apply((Match m, Improvement imp) item)
		{
			//IL_034a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0351: Expected O, but got Unknown
			//IL_01de: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0204: Expected O, but got Unknown
			//IL_023a: Unknown result type (might be due to invalid IL or missing references)
			//IL_023f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0240: Unknown result type (might be due to invalid IL or missing references)
			//IL_0247: Unknown result type (might be due to invalid IL or missing references)
			//IL_0248: Unknown result type (might be due to invalid IL or missing references)
			//IL_0254: Expected O, but got Unknown
			//IL_028a: Unknown result type (might be due to invalid IL or missing references)
			//IL_028f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0290: Unknown result type (might be due to invalid IL or missing references)
			//IL_0297: Unknown result type (might be due to invalid IL or missing references)
			//IL_0298: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a4: Expected O, but got Unknown
			//IL_02c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_02da: Expected O, but got Unknown
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: 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_0135: Expected O, but got Unknown
			//IL_02fe: Unknown result type (might be due to invalid IL or missing references)
			ImprovementsManager improvements = Game.Improvements;
			if ((Object)(object)improvements == (Object)null)
			{
				skipped++;
				return;
			}
			GameplayPlayerIdentity player = item.m.Player;
			if (player == null || ((Il2CppObjectBase)player).WasCollected)
			{
				skipped++;
				return;
			}
			ulong ownerClientId = ((NetworkBehaviour)player).OwnerClientId;
			Improvement item2 = item.imp;
			Recorder.Suppress = true;
			try
			{
				switch (item2.Kind)
				{
				case "Stats":
				{
					List<StatModifierMessage> list = new List<StatModifierMessage>();
					foreach (StatEntry item3 in item2.Stats ?? new List<StatEntry>())
					{
						if (Enum.TryParse<StatType>(item3.Stat, out StatType result7) && Enum.TryParse<StatModifiersType>(item3.Type, out StatModifiersType result8))
						{
							list.Add(new StatModifierMessage
							{
								Stat = result7,
								Value = item3.Value,
								Type = result8
							});
						}
					}
					if (list.Count == 0)
					{
						skipped++;
						return;
					}
					Il2CppReferenceArray<StatModifierMessage> val = new Il2CppReferenceArray<StatModifierMessage>((long)list.Count);
					for (int i = 0; i < list.Count; i++)
					{
						((Il2CppArrayBase<StatModifierMessage>)(object)val)[i] = list[i];
					}
					improvements.SendCharacterStatModifiersRpc(ownerClientId, val);
					break;
				}
				case "SpellStat":
				{
					if (!Enum.TryParse<SpellType>(item2.Spell, out SpellType result4) || !Enum.TryParse<StatType>(item2.Stat, out StatType result5))
					{
						skipped++;
						return;
					}
					improvements.SendSpellStatModifierRpc(ownerClientId, new SpellStatModifierMessage
					{
						Spell = result4,
						Stat = result5,
						Value = item2.Value
					});
					break;
				}
				case "Infusion":
				{
					if (!Enum.TryParse<SpellType>(item2.Spell, out SpellType result9) || !Enum.TryParse<ElementType>(item2.Element, out ElementType result10))
					{
						skipped++;
						return;
					}
					improvements.SendSpellInfusionRpc(ownerClientId, new SpellInfusionMessage
					{
						Spell = result9,
						Element = result10
					});
					break;
				}
				case "Variant":
				{
					if (!Enum.TryParse<SpellType>(item2.Spell, out SpellType result2) || !Enum.TryParse<SpellVariant>(item2.Variant, out SpellVariant result3))
					{
						skipped++;
						return;
					}
					improvements.SendSpellVariantRpc(ownerClientId, new SpellVariantMessage
					{
						Spell = result2,
						SpellVariant = result3
					});
					break;
				}
				case "Artifact":
				{
					if (!Enum.TryParse<ArtifactType>(item2.Artifact, out ArtifactType result6))
					{
						skipped++;
						return;
					}
					improvements.SendArtifactRpc(ownerClientId, new ArtifactMessage
					{
						Artifact = result6
					});
					break;
				}
				case "Final":
				{
					if (!Enum.TryParse<FinalUpgradeType>(item2.Final, out FinalUpgradeType result))
					{
						skipped++;
						return;
					}
					improvements.SendFinalUpgradeRpc(ownerClientId, result);
					break;
				}
				default:
					skipped++;
					return;
				}
				applied++;
				Recorder.Add(Game.KeyOf(player), item2);
			}
			catch (Exception ex)
			{
				skipped++;
				ManualLogSource log = Plugin.Log;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val2 = new BepInExErrorLogInterpolatedStringHandler(31, 3, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Restore: couldn't apply ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(item2.Kind);
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" for ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(Game.NameOf(player));
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(": ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(ex.Message);
				}
				log.LogError(val2);
			}
			finally
			{
				Recorder.Suppress = false;
			}
		}

		internal static int ApplyCounters(GameplayPlayerIdentity p, Dictionary<string, float> stacks, int revivesPerformed)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Expected O, but got Unknown
			int result = 0;
			try
			{
				result = Game.SetArtifactStacks(p, stacks);
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(27, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Restore (artifact stacks): ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
				}
				log.LogError(val);
			}
			try
			{
				if (revivesPerformed > p.RevivesPerformed)
				{
					p.RevivesPerformed = revivesPerformed;
				}
			}
			catch
			{
			}
			return result;
		}

		private static void SetLevelAndRevives(RunSave save)
		{
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Expected O, but got Unknown
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Expected O, but got Unknown
			revivesUsedSinceStart = false;
			bool flag = default(bool);
			try
			{
				PartyLevelManager party = Game.Party;
				if ((Object)(object)party != (Object)null)
				{
					party.level = Math.Max(1, save.PartyLevel);
					party.SetLevelRpc(party.level);
					if (party.currentExperience != null)
					{
						party.currentExperience.Value = save.PartyExperience;
					}
				}
				ReviveManager revives = Game.Revives;
				if (((revives != null) ? revives.CurrentRevives : null) != null)
				{
					revives.CurrentRevives.Value = save.Revives;
				}
				ManualLogSource log = Plugin.Log;
				BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(32, 2, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Restore: party level ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(save.PartyLevel);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(", revives ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(save.Revives);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(".");
				}
				log.LogInfo(val);
			}
			catch (Exception ex)
			{
				ManualLogSource log2 = Plugin.Log;
				BepInExErrorLogInterpolatedStringHandler val2 = new BepInExErrorLogInterpolatedStringHandler(25, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Restore (level/revives): ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<Exception>(ex);
				}
				log2.LogError(val2);
			}
		}

		[HarmonyPatch(typeof(ReviveManager), "UseReviveRpc")]
		[HarmonyPostfix]
		private static void ReviveUsed()
		{
			if (active != null)
			{
				revivesUsedSinceStart = true;
			}
		}

		private static void FinishUp()
		{
			//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ad: Expected O, but got Unknown
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Expected O, but got Unknown
			RunSave runSave = active;
			bool flag = default(bool);
			try
			{
				PartyLevelManager party = Game.Party;
				if ((Object)(object)party != (Object)null && runSave.PartyLevel > party.level)
				{
					party.level = runSave.PartyLevel;
					party.SetLevelRpc(runSave.PartyLevel);
				}
				ReviveManager revives = Game.Revives;
				if (((revives != null) ? revives.CurrentRevives : null) != null && revives.CurrentRevives.Value != runSave.Revives && !revivesUsedSinceStart)
				{
					revives.CurrentRevives.Value = runSave.Revives;
				}
				foreach (Match match in matches)
				{
					int num = ApplyCounters(match.Player, match.Saved.ArtifactStacks, match.Saved.RevivesPerformed);
					Net.SendCounters(((NetworkBehaviour)match.Player).OwnerClientId, match.Saved.ArtifactStacks, match.Saved.RevivesPerformed);
					if (num > 0)
					{
						ManualLogSource log = Plugin.Log;
						BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(31, 2, ref flag);
						if (flag)
						{
							((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Restore: ");
							((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(num);
							((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" artifact stacks for ");
							((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(Game.NameOf(match.Player));
							((BepInExLogInterpolatedStringHandler)val).AppendLiteral(".");
						}
						log.LogInfo(val);
					}
				}
				foreach (Match match2 in matches)
				{
					Recorder.Remember(match2.Player);
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log2 = Plugin.Log;
				BepInExErrorLogInterpolatedStringHandler val2 = new BepInExErrorLogInterpolatedStringHandler(25, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Restore (level/revives): ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<Exception>(ex);
				}
				log2.LogError(val2);
			}
			string text = $"Run restored: wave {runSave.Wave}, level {runSave.PartyLevel}, {applied} upgrades" + ((skipped > 0) ? $" ({skipped} skipped)" : "");
			Plugin.Log.LogInfo((object)text);
			Toast.Show(text + ((notes.Count > 0) ? ("\n" + string.Join("\n", notes)) : ""), (notes.Count > 0) ? 10f : 5f);
			active = null;
			phase = Phase.None;
		}
	}
	public class Improvement
	{
		public string Kind { get; set; }

		public string Spell { get; set; }

		public string Stat { get; set; }

		public float Value { get; set; }

		public string StatType { get; set; }

		public string Element { get; set; }

		public string Variant { get; set; }

		public string Artifact { get; set; }

		public string Final { get; set; }

		public List<StatEntry> Stats { get; set; }
	}
	public class StatEntry
	{
		public string Stat { get; set; }

		public float Value { get; set; }

		public string Type { get; set; }
	}
	public class SavedPlayer
	{
		public string Key { get; set; }

		public string Name { get; set; }

		public string Character { get; set; }

		public bool WasHost { get; set; }

		public List<string> Spells { get; set; } = new List<string>();

		public List<Improvement> Improvements { get; set; } = new List<Improvement>();

		public Dictionary<string, float> ArtifactStacks { get; set; } = new Dictionary<string, float>();

		public int RevivesPerformed { get; set; }
	}
	public class RunSave
	{
		public int Version { get; set; } = 1;

		public string SavedAt { get; set; }

		public string Reason { get; set; }

		public string World { get; set; }

		public string ConfigName { get; set; }

		public int Cycle { get; set; }

		public int Wave { get; set; }

		public int WaveCount { get; set; }

		public float RunTime { get; set; }

		public float CycleTime { get; set; }

		public int PartyLevel { get; set; }

		public float PartyExperience { get; set; }

		public int Revives { get; set; }

		public int CurseLevel { get; set; }

		public List<SavedPlayer> Players { get; set; } = new List<SavedPlayer>();

		public string Summary()
		{
			string value = ((Players.Count == 0) ? "no players" : string.Join(", ", Players.ConvertAll((SavedPlayer p) => p.Name + " (" + Pretty(p.Character) + ")")));
			return $"{World}  -  Wave {Wave}/{WaveCount}{((Cycle > 1) ? $" (loop {Cycle})" : "")}  -  Level {PartyLevel}\n{value}";
		}

		internal static string Pretty(string enumName)
		{
			if (string.IsNullOrEmpty(enumName))
			{
				return "?";
			}
			StringBuilder stringBuilder = new StringBuilder();
			for (int i = 0; i < enumName.Length; i++)
			{
				char c = enumName[i];
				if (i > 0 && char.IsUpper(c) && !char.IsUpper(enumName[i - 1]))
				{
					stringBuilder.Append(' ');
				}
				stringBuilder.Append(c);
			}
			return stringBuilder.ToString();
		}
	}
	internal static class SaveStore
	{
		private static readonly JsonSerializerOptions Options = new JsonSerializerOptions
		{
			WriteIndented = true
		};

		private static RunSave cached;

		private static DateTime cachedStamp;

		private static readonly object writeLock = new object();

		private static string pendingJson;

		private static bool writerRunning;

		private static string Dir => Path.Combine(Paths.ConfigPath, "RunSaves");

		private static string FilePath => Path.Combine(Dir, "save.json");

		private static string PrevPath => Path.Combine(Dir, "save.prev.json");

		internal static bool Exists => File.Exists(FilePath);

		internal static void Write(RunSave save)
		{
			string text = JsonSerializer.Serialize(save, Options);
			cached = save;
			cachedStamp = DateTime.MaxValue;
			lock (writeLock)
			{
				pendingJson = text;
				if (writerRunning)
				{
					return;
				}
				writerRunning = true;
			}
			Task.Run((Action)WriterLoop);
		}

		private static void WriterLoop()
		{
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Expected O, but got Unknown
			while (true)
			{
				string text;
				lock (writeLock)
				{
					text = pendingJson;
					pendingJson = null;
					if (text == null)
					{
						writerRunning = false;
						break;
					}
				}
				try
				{
					Directory.CreateDirectory(Dir);
					string text2 = FilePath + ".tmp";
					File.WriteAllText(text2, text);
					if (File.Exists(FilePath))
					{
						File.Copy(FilePath, PrevPath, overwrite: true);
					}
					File.Copy(text2, FilePath, overwrite: true);
					File.Delete(text2);
				}
				catch (Exception ex)
				{
					ManualLogSource log = Plugin.Log;
					bool flag;
					BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(25, 1, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Writing the save failed: ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
					}
					log.LogError(val);
				}
			}
		}

		internal static RunSave Read()
		{
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Expected O, but got Unknown
			try
			{
				if (!File.Exists(FilePath))
				{
					cached = null;
					return null;
				}
				DateTime lastWriteTimeUtc = File.GetLastWriteTimeUtc(FilePath);
				if (cached != null && (lastWriteTimeUtc == cachedStamp || cachedStamp == DateTime.MaxValue))
				{
					return cached;
				}
				cached = JsonSerializer.Deserialize<RunSave>(File.ReadAllText(FilePath));
				cachedStamp = lastWriteTimeUtc;
				return cached;
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(29, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Couldn't read the saved run: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
				}
				log.LogError(val);
				return null;
			}
		}

		internal static void Delete()
		{
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Expected O, but got Unknown
			lock (writeLock)
			{
				pendingJson = null;
			}
			try
			{
				if (File.Exists(FilePath))
				{
					File.Copy(FilePath, PrevPath, overwrite: true);
					File.Delete(FilePath);
				}
				cached = null;
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				bool flag;
				BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(31, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Couldn't delete the saved run: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
				}
				log.LogError(val);
			}
		}
	}
	[HarmonyPatch]
	internal static class Saver
	{
		internal static bool OwnsSave;

		private static bool autosavePending;

		private static float autosaveGiveUpAt;

		private static float lastSaveAt;

		internal static void OnRunStarted(bool restored)
		{
			lastSaveAt = Time.unscaledTime;
			autosavePending = false;
			OwnsSave = restored || !SaveStore.Exists;
		}

		internal static bool CanSave(out string why)
		{
			why = null;
			if (!Plugin.Enabled.Value)
			{
				why = "the mod is turned off";
			}
			else if (!Game.IsServer)
			{
				why = "only the host can save";
			}
			else if (!Recorder.RunActive || (Object)(object)Game.Run == (Object)null)
			{
				why = "no run in progress";
			}
			else if (Restorer.Busy)
			{
				why = "still restoring";
			}
			else if (Game.StoreBusy)
			{
				why = "finish the shop first";
			}
			return why == null;
		}

		internal static bool SaveManual(out string message)
		{
			bool num = Save("manual", out message);
			if (num)
			{
				OwnsSave = true;
			}
			return num;
		}

		internal static void Tick()
		{
			int value = Plugin.AutosaveIntervalSeconds.Value;
			if (value > 0 && Time.unscaledTime - lastSaveAt >= (float)value && Recorder.RunActive && !Restorer.Busy && !Game.StoreBusy && CanSave(out var why))
			{
				lastSaveAt = Time.unscaledTime;
				if (Save("auto", out why))
				{
					OwnsSave = true;
				}
			}
			if (autosavePending)
			{
				if (!Recorder.RunActive || Time.unscaledTime > autosaveGiveUpAt)
				{
					autosavePending = false;
				}
				else if (!Restorer.Busy && !Game.StoreBusy)
				{
					autosavePending = false;
					Autosave();
				}
			}
		}

		private static void Autosave()
		{
			if (Save("auto", out var message))
			{
				OwnsSave = true;
				lastSaveAt = Time.unscaledTime;
				if (Plugin.ToastOnAutosave.Value)
				{
					Toast.Show(message.Replace("Run saved", "Autosaved"), 2.5f);
				}
			}
		}

		internal static bool Save(string reason, out string message)
		{
			//IL_0515: Unknown result type (might be due to invalid IL or missing references)
			//IL_051c: Expected O, but got Unknown
			//IL_0413: Unknown result type (might be due to invalid IL or missing references)
			//IL_041a: Expected O, but got Unknown
			if (!CanSave(out var why))
			{
				message = "Can't save: " + why + ".";
				return false;
			}
			try
			{
				RunManager run = Game.Run;
				RunSave obj = new RunSave
				{
					SavedAt = DateTime.Now.ToString("yyyy-MM-dd HH:mm"),
					Reason = reason,
					World = Game.WorldName(run),
					ConfigName = Game.ConfigName(run),
					Cycle = (run.CycleNumber?.Value ?? 1),
					Wave = Math.Max(1, run.CurrentWave?.Value ?? 1)
				};
				RunConfiguration configuration = run.Configuration;
				obj.WaveCount = ((configuration == null) ? ((int?)null) : configuration.Waves?.Count).GetValueOrDefault();
				obj.RunTime = run.currentRunTimeInSeconds;
				obj.CycleTime = run.currentCycleTimeInSeconds;
				RunSave runSave = obj;
				PartyLevelManager party = Game.Party;
				if ((Object)(object)party != (Object)null)
				{
					runSave.PartyLevel = party.level;
					runSave.PartyExperience = party.currentExperience?.Value ?? 0f;
				}
				ReviveManager revives = Game.Revives;
				if ((Object)(object)revives != (Object)null)
				{
					runSave.Revives = revives.CurrentRevives?.Value ?? 0;
				}
				List<GameplayPlayerIdentity> list = Game.Players();
				foreach (GameplayPlayerIdentity item3 in list)
				{
					Recorder.Remember(item3);
				}
				try
				{
					GameplayPlayerIdentity val = Game.LocalPlayer();
					if ((Object)(object)val != (Object)null)
					{
						runSave.CurseLevel = val.ActiveCurseLevel;
					}
				}
				catch
				{
				}
				List<string> list2 = new List<string>();
				foreach (GameplayPlayerIdentity item4 in list)
				{
					string item = Game.KeyOf(item4);
					if (!list2.Contains(item))
					{
						list2.Add(item);
					}
				}
				foreach (string key in Recorder.Log.Keys)
				{
					if (!list2.Contains(key))
					{
						list2.Add(key);
					}
				}
				foreach (string item5 in list2)
				{
					Recorder.Known.TryGetValue(item5, out var value);
					List<Improvement> value2;
					SavedPlayer item2 = new SavedPlayer
					{
						Key = item5,
						Name = (value?.Name ?? item5),
						Character = (value?.Character ?? ""),
						WasHost = (value?.WasHost ?? false),
						Spells = ((value?.Spells != null) ? new List<string>(value.Spells) : new List<string>()),
						Improvements = (Recorder.Log.TryGetValue(item5, out value2) ? new List<Improvement>(value2) : new List<Improvement>()),
						ArtifactStacks = ((value?.ArtifactStacks != null) ? new Dictionary<string, float>(value.ArtifactStacks) : new Dictionary<string, float>()),
						RevivesPerformed = (value?.RevivesPerformed ?? 0)
					};
					runSave.Players.Add(item2);
				}
				SaveStore.Write(runSave);
				int num = 0;
				foreach (SavedPlayer player in runSave.Players)
				{
					num += player.Improvements.Count;
				}
				message = $"Run saved - wave {runSave.Wave}, level {runSave.PartyLevel}";
				ManualLogSource log = Plugin.Log;
				bool flag = default(bool);
				BepInExInfoLogInterpolatedStringHandler val2 = new BepInExInfoLogInterpolatedStringHandler(60, 8, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Saved run (");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(reason);
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("): ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(runSave.World);
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(", cycle ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(runSave.Cycle);
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(", wave ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(runSave.Wave);
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("/");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(runSave.WaveCount);
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(", ");
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("level ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(runSave.PartyLevel);
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(", ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(runSave.Players.Count);
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" players, ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(num);
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" upgrades.");
				}
				log.LogInfo(val2);
				return true;
			}
			catch (Exception ex)
			{
				ManualLogSource log2 = Plugin.Log;
				bool flag2 = default(bool);
				BepInExErrorLogInterpolatedStringHandler val3 = new BepInExErrorLogInterpolatedStringHandler(13, 1, ref flag2);
				if (flag2)
				{
					((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("Save failed: ");
					((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<Exception>(ex);
				}
				log2.LogError(val3);
				message = "Save failed (see the BepInEx log).";
				return false;
			}
		}

		[HarmonyPatch(typeof(RunManager), "StartWave")]
		[HarmonyPostfix]
		private static void WaveStarted(RunManager __instance)
		{
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Expected O, but got Unknown
			try
			{
				if (Plugin.AutosaveEachWave.Value && !Restorer.Busy && Recorder.RunActive && (__instance.CurrentWave?.Value ?? 0) > 1)
				{
					if (Game.StoreBusy)
					{
						autosavePending = true;
						autosaveGiveUpAt = Time.unscaledTime + 60f;
					}
					else
					{
						Autosave();
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(10, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Autosave: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Exception>(ex);
				}
				log.LogError(val);
			}
		}

		[HarmonyPatch(typeof(ConfirmQuitPanel), "Quit")]
		[HarmonyPrefix]
		private static void Quitting()
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Expected O, but got Unknown
			try
			{
				if (Plugin.SaveOnQuit.Value && CanSave(out var why) && Save("quit", out why))
				{
					OwnsSave = true;
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(14, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Save on quit: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Exception>(ex);
				}
				log.LogError(val);
			}
		}
	}
	internal static class UiKit
	{
		internal static TMP_FontAsset Font()
		{
			TMP_FontAsset val = null;
			try
			{
				TextMeshProUGUI val2 = Object.FindObjectOfType<TextMeshProUGUI>();
				if ((Object)(object)val2 != (Object)null)
				{
					val = ((TMP_Text)val2).font;
				}
				if ((Object)(object)val == (Object)null)
				{
					val = TMP_Settings.defaultFontAsset;
				}
			}
			catch
			{
			}
			return val;
		}

		internal static GameObject Canvas(string name, int order)
		{
			//IL_0001: 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_000c: Expected O, but got Unknown
			//IL_000c: 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_0037: 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)
			//IL_0053: Expected O, but got Unknown
			GameObject val = new GameObject(name);
			Object.DontDestroyOnLoad((Object)val);
			Canvas obj = val.AddComponent<Canvas>();
			obj.renderMode = (RenderMode)0;
			obj.sortingOrder = order;
			CanvasScaler obj2 = val.AddComponent<CanvasScaler>();
			obj2.uiScaleMode = (ScaleMode)1;
			obj2.referenceResolution = new Vector2(1920f, 1080f);
			obj2.matchWidthOrHeight = 0.5f;
			val.AddComponent<GraphicRaycaster>();
			return val;
		}

		internal static GameObject Rect(string name, Transform parent, Vector2 pos, Vector2 size)
		{
			//IL_0001: 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_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Expected O, but got Unknown
			GameObject val = new GameObject(name);
			RectTransform val2 = val.AddComponent<RectTransform>();
			val.transform.SetParent(parent, false);
			val2.anchoredPosition = pos;
			val2.sizeDelta = size;
			return val;
		}

		internal static TextMeshProUGUI Text(string name, Transform parent, TMP_FontAsset font, float size, Vector2 pos, Vector2 box, TextAlignmentOptions align = (TextAlignmentOptions)514)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0004: 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_0043: Unknown result type (might be due to invalid IL or missing references)
			TextMeshProUGUI obj = Rect(name, parent, pos, box).AddComponent<TextMeshProUGUI>();
			((TMP_Text)obj).font = font;
			((TMP_Text)obj).fontSize = size;
			((TMP_Text)obj).alignment = align;
			((TMP_Text)obj).textWrappingMode = (TextWrappingModes)1;
			((TMP_Text)obj).overflowMode = (TextOverflowModes)0;
			((TMP_Text)obj).richText = true;
			((Graphic)obj).raycastTarget = false;
			((Graphic)obj).color = Color.white;
			return obj;
		}

		internal static (Button button, TextMeshProUGUI label) Button(string name, Transform parent, TMP_FontAsset font, string label, Vector2 pos, Vector2 size, UnityAction onClick, Color color)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: 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)
			GameObject val = Rect(name, parent, pos, size);
			Image val2 = val.AddComponent<Image>();
			((Graphic)val2).color = color;
			Button obj = val.AddComponent<Button>();
			((Selectable)obj).targetGraphic = (Graphic)(object)val2;
			((UnityEvent)obj.onClick).AddListener(onClick);
			TextMeshProUGUI val3 = Text("Label", val.transform, font, 22f, Vector2.zero, size, (TextAlignmentOptions)514);
			((TMP_Text)val3).text = label;
			return (button: obj, label: val3);
		}

		internal static bool Pressed(string keyName)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			Keyboard current = Keyboard.current;
			if (current == null || string.IsNullOrEmpty(keyName))
			{
				return false;
			}
			if (!Enum.TryParse<Key>(keyName, ignoreCase: true, out Key result) || (int)result == 0)
			{
				return false;
			}
			KeyControl val = current[result];
			if (val != null)
			{
				return ((ButtonControl)val).wasPressedThisFrame;
			}
			return false;
		}
	}
	internal static class Toast
	{
		private static GameObject canvas;

		private static GameObject panel;

		private static TextMeshProUGUI text;

		private static float hideAt;

		private const float ToastWidth = 360f;

		internal static void Show(string message, float seconds)
		{
			//IL_01cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d3: Expected O, but got Unknown
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: 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_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: 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_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_017e: Unknown result type (might be due to invalid IL or missing references)
			//IL_019e: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if ((Object)(object)panel == (Object)null)
				{
					TMP_FontAsset val = UiKit.Font();
					if ((Object)(object)val == (Object)null)
					{
						Plugin.Log.LogInfo((object)message);
						return;
					}
					canvas = UiKit.Canvas("RunSaves_Toast", 520);
					panel = UiKit.Rect("Toast", canvas.transform, Vector2.zero, new Vector2(360f, 40f));
					RectTransform component = panel.GetComponent<RectTransform>();
					Vector2 val2 = (component.pivot = new Vector2(1f, 1f));
					Vector2 anchorMin = (component.anchorMax = val2);
					component.anchorMin = anchorMin;
					component.anchoredPosition = new Vector2(-20f, -20f);
					Image obj = panel.AddComponent<Image>();
					((Graphic)obj).color = new Color(0.08f, 0.05f, 0.14f, 0.85f);
					((Graphic)obj).raycastTarget = false;
					text = UiKit.Text("Text", panel.transform, val, 15f, Vector2.zero, new Vector2(340f, 30f), (TextAlignmentOptions)513);
				}
				((TMP_Text)text).text = message;
				float num = 0f;
				try
				{
					num = ((TMP_Text)text).GetPreferredValues(message, 340f, 0f).y;
				}
				catch
				{
				}
				if (num <= 0f)
				{
					num = (float)message.Split('\n').Length * 19f;
				}
				panel.GetComponent<RectTransform>().sizeDelta = new Vector2(360f, num + 14f);
				((Component)text).GetComponent<RectTransform>().sizeDelta = new Vector2(340f, num + 4f);
				canvas.SetActive(true);
				hideAt = Time.unscaledTime + seconds;
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val5 = new BepInExErrorLogInterpolatedStringHandler(7, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val5).AppendLiteral("Toast: ");
					((BepInExLogInterpolatedStringHandler)val5).AppendFormatted<string>(ex.Message);
				}
				log.LogError(val5);
			}
		}

		internal static void Tick()
		{
			if ((Object)(object)canvas != (Object)null && canvas.activeSelf && Time.unscaledTime >= hideAt)
			{
				canvas.SetActive(false);
			}
		}
	}
	[HarmonyPatch]
	internal static class PauseButton
	{
		private static GameObject button;

		private static TextMeshProUGUI label;

		private static UnityAction click;

		private static float resetLabelAt;

		[HarmonyPatch(typeof(PauseMenuManager), "Open")]
		[HarmonyPostfix]
		private static void Opened(PauseMenuManager __instance)
		{
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Expected O, but got Unknown
			try
			{
				bool flag = Plugin.Enabled.Value && Plugin.ShowSaveButton.Value && Game.IsServer && Recorder.RunActive;
				if (!Alive((Object)(object)button) || !SameMenu(__instance))
				{
					button = null;
					label = null;
					if (flag)
					{
						Build(__instance);
					}
				}
				if ((Object)(object)button != (Object)null)
				{
					button.SetActive(flag);
				}
				if ((Object)(object)label != (Object)null)
				{
					((TMP_Text)label).text = "Save Run";
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				bool flag2 = default(bool);
				BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(19, 1, ref flag2);
				if (flag2)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Pause menu button: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Exception>(ex);
				}
				log.LogError(val);
			}
		}

		private static bool SameMenu(PauseMenuManager menu)
		{
			Button val = (((Object)(object)menu.settingsButton != (Object)null) ? menu.settingsButton : menu.resumeButton);
			if ((Object)(object)val != (Object)null)
			{
				return (Object)(object)button.transform.parent == (Object)(object)((Component)val).transform.parent;
			}
			return false;
		}

		private static bool Alive(Object o)
		{
			if (o != null && !((Il2CppObjectBase)o).WasCollected)
			{
				return o != (Object)null;
			}
			return false;
		}

		private static void Build(PauseMenuManager menu)
		{
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Expected O, but got Unknown
			Button val = (((Object)(object)menu.settingsButton != (Object)null) ? menu.settingsButton : menu.resumeButton);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			button = Object.Instantiate<GameObject>(((Component)val).gameObject, ((Component)val).transform.parent);
			((Object)button).name = "RunSaves_SaveButton";
			button.transform.SetSiblingIndex(((Component)val).transform.GetSiblingIndex() + 1);
			foreach (Component componentsInChild in button.GetComponentsInChildren<Component>(true))
			{
				if ((Object)(object)componentsInChild != (Object)null && ((MemberInfo)((Object)componentsInChild).GetIl2CppType()).Name == "LocalizeStringEvent")
				{
					Object.Destroy((Object)(object)componentsInChild);
				}
			}
			Button component = button.GetComponent<Button>();
			component.onClick = new ButtonClickedEvent();
			if (click == null)
			{
				click = DelegateSupport.ConvertDelegate<UnityAction>((Delegate)new Action(OnClick));
			}
			((UnityEvent)component.onClick).AddListener(click);
			label = button.GetComponentInChildren<TextMeshProUGUI>(true);
			if ((Object)(object)label != (Object)null)
			{
				((TMP_Text)label).text = "Save Run";
			}
			Plugin.Log.LogInfo((object)"Added Save Run to the pause menu.");
		}

		private static void OnClick()
		{
			string message;
			bool flag = Saver.SaveManual(out message);
			Toast.Show(message, 3f);
			if ((Object)(object)label != (Object)null)
			{
				((TMP_Text)label).text = (flag ? "Saved!" : "Can't save");
				resetLabelAt = Time.unscaledTime + 2f;
			}
		}

		internal static void Tick()
		{
			if (resetLabelAt > 0f && Time.unscaledTime >= resetLabelAt)
			{
				resetLabelAt = 0f;
				if (Alive((Object)(object)label))
				{
					((TMP_Text)label).text = "Save Run";
				}
			}
		}
	}
	public class RunSavesUI : MonoBehaviour
	{
		private GameObject canvas;

		private GameObject panel;

		private GameObject tab;

		private TextMeshProUGUI title;

		private TextMeshProUGUI body;

		private TextMeshProUGUI status;

		private TextMeshProUGUI resumeLabel;

		private TextMeshProUGUI deleteLabel;

		private TextMeshProUGUI hideLabel;

		private bool hidden;

		private bool inMenu;

		private float nextScan;

		private float deleteConfirmUntil;

		private float nextErrorLog;

		private static UnityAction resumeAct;

		private static UnityAction deleteAct;

		private static UnityAction hideAct;

		private static UnityAction showAct;

		private static RunSavesUI instance;

		public RunSavesUI(IntPtr ptr)
			: base(ptr)
		{
		}

		private void Update()
		{
			instance = this;
			Step("restore", Restorer.Tick);
			Step("autosave", Saver.Tick);
			Step("client spells", Net.TryEquipPending);
			Step("toast", Toast.Tick);
			Step("pause", PauseButton.Tick);
			Step("save key", delegate
			{
				if (UiKit.Pressed(Plugin.SaveKey.Value) && Saver.CanSave(out var _))
				{
					Saver.SaveManual(out var message);
					Toast.Show(message, 3f);
				}
			});
			if (Time.unscaledTime >= nextScan)
			{
				nextScan = Time.unscaledTime + 0.5f;
				Step("menu check", delegate
				{
					inMenu = Game.InMainMenu();
				});
			}
			bool available = inMenu && Plugin.Enabled.Value && Plugin.ShowLobbyPrompt.Value && !Game.IsClientOnly && (SaveStore.Exists || Restorer.Armed != null);
			if (available)
			{
				Step("panel key", delegate
				{
					if (UiKit.Pressed(Plugin.PanelKey.Value))
					{
						hidden = !hidden;
					}
				});
			}
			if (available && (Object)(object)panel == (Object)null)
			{
				Step("build", Build);
			}
			if ((Object)(object)canvas != (Object)null && canvas.activeSelf != available)
			{
				Step("show/hide", delegate
				{
					canvas.SetActive(available);
				});
			}
			if (available && (Object)(object)panel != (Object)null)
			{
				if (panel.activeSelf == hidden)
				{
					panel.SetActive(!hidden);
				}
				if ((Object)(object)tab != (Object)null && tab.activeSelf != hidden)
				{
					tab.SetActive(hidden);
				}
				if (!hidden && Time.frameCount % 15 == 0)
				{
					Step("refresh", Refresh);
				}
			}
		}

		private void Step(string what, Action action)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Expected O, but got Unknown
			try
			{
				action();
			}
			catch (Exception ex)
			{
				if (!(Time.unscaledTime < nextErrorLog))
				{
					nextErrorLog = Time.unscaledTime + 10f;
					ManualLogSource log = Plugin.Log;
					bool flag = default(bool);
					BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(7, 2, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("UI (");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(what);
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("): ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Exception>(ex);
					}
					log.LogError(val);
				}
			}
		}

		private void Build()
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: 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_00bd: 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_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: 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_0183: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_02dd: Unknown res