Decompiled source of QualityOfRoR2 v1.1.1

plugins/QualityOfRoR2/QualityOfRoR2.dll

Decompiled 10 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using EntityStates;
using EntityStates.Barrel;
using HG.Reflection;
using Microsoft.CodeAnalysis;
using On.RoR2;
using On.RoR2.UI;
using QualityOfRoR2.DebugCommands;
using RoR2;
using RoR2.UI;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Networking;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: OptIn]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("QualityOfRoR2")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("QualityOfRoR2")]
[assembly: AssemblyTitle("QualityOfRoR2")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace QualityOfRoR2
{
	[BepInPlugin("modwright.qualityofror2", "Quality of RoR2", "1.1.1")]
	public sealed class QualityPlugin : BaseUnityPlugin
	{
		private static readonly ICommandModule[] commandModules = new ICommandModule[5]
		{
			new StageCommands(),
			new StageListCommands(),
			new PlayerCommands(),
			new GoldCommands(),
			new RestartCommands()
		};

		private const string CommandHelp = "modwright stages [filter]\nmodwright stage <scene-name>\nmodwright players\nmodwright gold <amount> [self|all|#id|\"player name\"]\nmodwright restart";

		private SelectionStore preferences;

		private string preferenceProfile;

		private string preferenceSession;

		private bool restoringPreferences;

		private float nextPreferenceSave;

		private float restoreDeadline;

		private float nextRestoreCheck;

		private readonly Dictionary<string, string> pendingPreferences = new Dictionary<string, string>();

		private readonly HashSet<PreGameRuleVoteController> readyPreferenceVotes = new HashSet<PreGameRuleVoteController>();

		internal static QualityPlugin Instance;

		internal ConfigEntry<bool> HideChests;

		internal ConfigEntry<bool> AutoSprint;

		internal ConfigEntry<bool> QuickRestart;

		internal ConfigEntry<bool> RespectExisting;

		internal ConfigEntry<float> ChestDelay;

		internal ConfigEntry<bool> WoodRevive;

		internal ConfigEntry<bool> RememberSelections;

		private ConfigEntry<bool> reuseSeed;

		private bool otherChests;

		private bool otherSprint;

		private bool otherRestart;

		private bool hooked;

		private readonly RestartGate restartGate = new RestartGate();

		private Run restartingRun;

		internal bool ChestFeature
		{
			get
			{
				if (HideChests.Value)
				{
					if (RespectExisting.Value)
					{
						return !otherChests;
					}
					return true;
				}
				return false;
			}
		}

		private bool SprintFeature
		{
			get
			{
				if (AutoSprint.Value)
				{
					if (RespectExisting.Value)
					{
						return !otherSprint;
					}
					return true;
				}
				return false;
			}
		}

		internal bool RestartButton
		{
			get
			{
				if (QuickRestart.Value)
				{
					if (RespectExisting.Value)
					{
						return !otherRestart;
					}
					return true;
				}
				return false;
			}
		}

		private void ApplyAutoSprint(orig_PollButtonInput orig, PlayerCharacterMasterController controller)
		{
			orig.Invoke(controller);
			if (!SprintFeature || !controller.hasEffectiveAuthority || !Object.op_Implicit((Object)(object)controller.master))
			{
				return;
			}
			CharacterBody body = controller.master.GetBody();
			LocalUser val = (Object.op_Implicit((Object)(object)controller.networkUser) ? controller.networkUser.localUser : null);
			if (Object.op_Implicit((Object)(object)body) && Object.op_Implicit((Object)(object)body.inputBank) && val != null && !val.isUIFocused)
			{
				CameraRigController cameraRigController = controller.networkUser.cameraRigController;
				if (Object.op_Implicit((Object)(object)cameraRigController) && cameraRigController.isControlAllowed)
				{
					bool walkHeld = val.inputPlayer != null && val.inputPlayer.GetButton(18);
					InputBankTest inputBank = body.inputBank;
					SkillLocator skillLocator = body.skillLocator;
					bool blockingSkill = Object.op_Implicit((Object)(object)skillLocator) && (BlocksSprint(skillLocator.primary, inputBank.skill1.down) || BlocksSprint(skillLocator.secondary, inputBank.skill2.down) || BlocksSprint(skillLocator.utility, inputBank.skill3.down) || BlocksSprint(skillLocator.special, inputBank.skill4.down));
					bool flag = FeatureRules.Sprint(enabled: true, Object.op_Implicit((Object)(object)body.healthComponent) && body.healthComponent.alive, controlAllowed: true, ((Vector3)(ref inputBank.moveVector)).sqrMagnitude > 0.25f, walkHeld, body.CheckHandlingConfig((BodyHandlingOption)10, false), blockingSkill);
					((ButtonState)(ref inputBank.sprint)).PushState(flag);
				}
			}
		}

		private static bool BlocksSprint(GenericSkill skill, bool held)
		{
			if (!Object.op_Implicit((Object)(object)skill) || !Object.op_Implicit((Object)(object)skill.skillDef) || skill.skillDef.forceSprintDuringState)
			{
				return false;
			}
			if (skill.skillDef.cancelSprintingOnActivation)
			{
				if (!held)
				{
					if (Object.op_Implicit((Object)(object)skill.stateMachine) && skill.stateMachine.state != null)
					{
						return skill.skillDef.IsAlreadyInState(skill);
					}
					return false;
				}
				return true;
			}
			return false;
		}

		[ConCommand(/*Could not decode attribute arguments.*/)]
		private static void DeveloperCommand(ConCommandArgs args)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			ExecuteConsoleCommand(args);
		}

		[ConCommand(/*Could not decode attribute arguments.*/)]
		private static void GoldCommand(ConCommandArgs args)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			ExecuteConsoleCommand(args);
		}

		[ConCommand(/*Could not decode attribute arguments.*/)]
		private static void BluePortalCommand(ConCommandArgs args)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			ExecuteConsoleCommand(args);
		}

		[ConCommand(/*Could not decode attribute arguments.*/)]
		private static void StageCommand(ConCommandArgs args)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			ExecuteConsoleCommand(args);
		}

		[ConCommand(/*Could not decode attribute arguments.*/)]
		private static void RestartCommand(ConCommandArgs args)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			ExecuteConsoleCommand(args);
		}

		private static void ExecuteConsoleCommand(ConCommandArgs args)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			((ConCommandArgs)(ref args)).Log((object)(Object.op_Implicit((Object)(object)Instance) ? Instance.ExecuteDebug(CommandText.FromArguments(args.commandName, args.userArgs.ToArray()), args.sender) : "Quality of RoR2 is not active."));
		}

		private string ExecuteDebug(DebugRequest request, NetworkUser sender)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			object obj;
			if (!Object.op_Implicit((Object)(object)sender))
			{
				LocalUser firstLocalUser = LocalUserManager.GetFirstLocalUser();
				obj = ((firstLocalUser != null) ? firstLocalUser.currentNetworkUser : null);
			}
			else
			{
				obj = sender;
			}
			NetworkUser val = (NetworkUser)obj;
			if (!Object.op_Implicit((Object)(object)val) || !OwnerAccess.Allows(val.id.value, val.localUser != null && ((NetworkBehaviour)val).isLocalPlayer, NetworkServer.active))
			{
				return "These developer commands are private to the owner's Steam account while hosting.";
			}
			if (request.Error != null)
			{
				return request.Error;
			}
			if (request.Verb == "help")
			{
				if (request.Arguments.Length != 0)
				{
					return "Usage: modwright help";
				}
				return "modwright stages [filter]\nmodwright stage <scene-name>\nmodwright players\nmodwright gold <amount> [self|all|#id|\"player name\"]\nmodwright restart";
			}
			ICommandModule commandModule = commandModules.FirstOrDefault((ICommandModule m) => m.Name == request.Verb);
			if (commandModule != null)
			{
				return commandModule.Execute(new CommandContext
				{
					Plugin = this,
					Caller = val
				}, request.Arguments);
			}
			return "Unknown subcommand.\nmodwright stages [filter]\nmodwright stage <scene-name>\nmodwright players\nmodwright gold <amount> [self|all|#id|\"player name\"]\nmodwright restart";
		}

		private void SubmitDebugChat(orig_SubmitChat orig, ChatBox chat)
		{
			if (!Object.op_Implicit((Object)(object)chat.inputField) || !CommandText.TryParse(chat.inputField.text, out var request))
			{
				orig.Invoke(chat);
				return;
			}
			chat.inputField.text = "";
			chat.SetShowInput(false);
			Chat.AddMessage("<color=#B5E4D4>[Quality of RoR2]</color> " + ExecuteDebug(request, null));
		}

		private void InitializePreferences(orig_Start orig, PreGameRuleVoteController votes)
		{
			orig.Invoke(votes);
			readyPreferenceVotes.Add(votes);
		}

		private bool EnsurePreferences(LocalUser user)
		{
			string text = ((user == null) ? null : user.userProfile?.fileName);
			if (string.IsNullOrEmpty(text))
			{
				return false;
			}
			if (preferences != null && preferenceProfile == text)
			{
				return true;
			}
			preferences?.Flush();
			preferenceProfile = text;
			preferences = new SelectionStore(SelectionStore.ProfileDirectory(text));
			preferenceSession = null;
			pendingPreferences.Clear();
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Local lobby selections: " + preferences.Path));
			return true;
		}

		private void RememberVote(orig_SetVote orig, PreGameRuleVoteController votes, int ruleIndex, int choiceValue)
		{
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			orig.Invoke(votes, ruleIndex, choiceValue);
			if (!RememberSelections.Value || restoringPreferences || !readyPreferenceVotes.Contains(votes) || !Object.op_Implicit((Object)(object)PreGameController.instance) || !Object.op_Implicit((Object)(object)votes.networkUserNetworkIdentity))
			{
				return;
			}
			NetworkUser component = ((Component)votes.networkUserNetworkIdentity).GetComponent<NetworkUser>();
			if (!Object.op_Implicit((Object)(object)component) || component.localUser == null || !EnsurePreferences(component.localUser))
			{
				return;
			}
			RuleDef ruleDef = RuleCatalog.GetRuleDef(ruleIndex);
			if (ruleDef != null && SelectionStore.Relevant(ruleDef.globalName) && choiceValue >= -1 && choiceValue < ruleDef.choices.Count)
			{
				RuleChoiceDef val = ((choiceValue >= 0) ? ruleDef.choices[choiceValue] : null);
				if (val == null || ((SerializableBitArray)PreGameController.instance.resolvedRuleChoiceMask)[val.globalIndex])
				{
					pendingPreferences.Clear();
					preferences.Remember(GameModeCatalog.GetGameModeName(PreGameController.instance.gameModeIndex), ruleDef.globalName, val?.globalName ?? "@neutral");
					nextPreferenceSave = Time.unscaledTime + 0.4f;
				}
			}
		}

		private unsafe void RememberRunSelections(Run run)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			if (!RememberSelections.Value || !NetworkServer.active || !EnsurePreferences(LocalUserManager.GetFirstLocalUser()))
			{
				return;
			}
			string gameModeName = GameModeCatalog.GetGameModeName(run.gameModeIndex);
			ChoicesEnumerable choices = run.ruleBook.choices;
			Enumerator enumerator = ((ChoicesEnumerable)(ref choices)).GetEnumerator();
			try
			{
				while (((Enumerator)(ref enumerator)).MoveNext())
				{
					RuleChoiceDef current = ((Enumerator)(ref enumerator)).Current;
					if (current.ruleDef.globalName == "Difficulty")
					{
						preferences.Remember(gameModeName, current.ruleDef.globalName, current.globalName);
					}
				}
			}
			finally
			{
				((IDisposable)(*(Enumerator*)(&enumerator))/*cast due to .constrained prefix*/).Dispose();
			}
			preferences.Flush();
			pendingPreferences.Clear();
		}

		private void UpdatePreferences()
		{
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			if (!RememberSelections.Value || !EnsurePreferences(LocalUserManager.GetFirstLocalUser()))
			{
				return;
			}
			if (preferences.Dirty && Time.unscaledTime >= nextPreferenceSave)
			{
				preferences.Flush();
				nextPreferenceSave = Time.unscaledTime + 2f;
			}
			PreGameController instance = PreGameController.instance;
			LocalUser firstLocalUser = LocalUserManager.GetFirstLocalUser();
			NetworkUser val = ((firstLocalUser != null) ? firstLocalUser.currentNetworkUser : null);
			if (!Object.op_Implicit((Object)(object)instance) || Object.op_Implicit((Object)(object)Run.instance) || !NetworkServer.active || !Object.op_Implicit((Object)(object)val))
			{
				preferenceSession = null;
				pendingPreferences.Clear();
				return;
			}
			PreGameRuleVoteController val2 = PreGameRuleVoteController.FindForUser(val);
			readyPreferenceVotes.RemoveWhere((PreGameRuleVoteController v) => !Object.op_Implicit((Object)(object)v));
			if (!Object.op_Implicit((Object)(object)val2) || !readyPreferenceVotes.Contains(val2))
			{
				return;
			}
			string gameModeName = GameModeCatalog.GetGameModeName(instance.gameModeIndex);
			string text = ((Object)instance).GetInstanceID() + ":" + gameModeName;
			if (preferenceSession != text)
			{
				preferenceSession = text;
				pendingPreferences.Clear();
				SavedChoice[] array = preferences.ForMode(gameModeName);
				foreach (SavedChoice savedChoice in array)
				{
					pendingPreferences[savedChoice.rule] = savedChoice.choice;
				}
				restoreDeadline = Time.unscaledTime + 5f;
				nextRestoreCheck = 0f;
			}
			if (pendingPreferences.Count == 0 || Time.unscaledTime < nextRestoreCheck)
			{
				return;
			}
			nextRestoreCheck = Time.unscaledTime + 0.1f;
			restoringPreferences = true;
			try
			{
				KeyValuePair<string, string>[] array2 = pendingPreferences.ToArray();
				for (int num = 0; num < array2.Length; num++)
				{
					KeyValuePair<string, string> keyValuePair = array2[num];
					if (keyValuePair.Value == "@neutral")
					{
						for (int num2 = 0; num2 < RuleCatalog.ruleCount; num2++)
						{
							if (RuleCatalog.GetRuleDef(num2).globalName == keyValuePair.Key)
							{
								val2.SetVote(num2, -1);
								break;
							}
						}
						pendingPreferences.Remove(keyValuePair.Key);
					}
					else
					{
						RuleChoiceDef val3 = RuleCatalog.FindChoiceDef(keyValuePair.Value);
						if (val3 == null || val3.ruleDef.globalName != keyValuePair.Key)
						{
							pendingPreferences.Remove(keyValuePair.Key);
						}
						else if (((SerializableBitArray)instance.resolvedRuleChoiceMask)[val3.globalIndex])
						{
							val2.SetVote(val3.ruleDef.globalIndex, val3.localIndex);
							pendingPreferences.Remove(keyValuePair.Key);
						}
					}
				}
			}
			finally
			{
				restoringPreferences = false;
			}
			if (Time.unscaledTime >= restoreDeadline)
			{
				pendingPreferences.Clear();
			}
		}

		private void ObserveChestState(orig_SetState orig, EntityStateMachine machine, EntityState state)
		{
			orig.Invoke(machine, state);
			if (ChestFeature && Object.op_Implicit((Object)(object)machine) && OpenedChestVisual.IsOpened(state) && (Object.op_Implicit((Object)(object)((Component)machine).GetComponent<ChestBehavior>()) || Object.op_Implicit((Object)(object)((Component)machine).GetComponent<OptionChestBehavior>()) || Object.op_Implicit((Object)(object)((Component)machine).GetComponent<RouletteChestController>()) || Object.op_Implicit((Object)(object)((Component)machine).GetComponent<BarrelInteraction>())))
			{
				OpenedChestVisual openedChestVisual = ((Component)machine).GetComponent<OpenedChestVisual>();
				if (!Object.op_Implicit((Object)(object)openedChestVisual))
				{
					openedChestVisual = ((Component)machine).gameObject.AddComponent<OpenedChestVisual>();
				}
				openedChestVisual.Opened(machine);
			}
		}

		private void Awake()
		{
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Expected O, but got Unknown
			Instance = this;
			HideChests = ((BaseUnityPlugin)this).Config.Bind<bool>("Chests", "HideOpened", true, "Hide opened ordinary, large, legendary and option chests after their reward animation. Opened barrels are included; reopened or Delusion-reset chests reappear.");
			ChestDelay = ((BaseUnityPlugin)this).Config.Bind<float>("Chests", "HideDelay", 0.75f, new ConfigDescription("Seconds after the opened state before hiding the chest.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), Array.Empty<object>()));
			AutoSprint = ((BaseUnityPlugin)this).Config.Bind<bool>("Movement", "AutoSprint", true, "Sprint automatically while moving. Hold your Sprint binding to walk. Native skill restrictions and flying controls are respected.");
			QuickRestart = ((BaseUnityPlugin)this).Config.Bind<bool>("Restart", "Enabled", true, "Add Quick restart to the host pause and game-over menus. There is no restart hotkey.");
			WoodRevive = ((BaseUnityPlugin)this).Config.Bind<bool>("Shrines", "WoodsRevivesTeammates", true, "Each paid Shrine of the Woods activation also revives dead connected teammates nearby. Native healing, cost and purchase limit remain unchanged.");
			RememberSelections = ((BaseUnityPlugin)this).Config.Bind<bool>("Lobby", "RememberDifficultyAndArtifacts", true, "Remember difficulty and artifacts per local RoR2 profile and game mode. Restore only available choices when hosting a lobby.");
			reuseSeed = ((BaseUnityPlugin)this).Config.Bind<bool>("Restart", "ReuseSeed", false, "Keep the original RNG seed on restart. False creates a fresh roll with the same character, loadout, difficulty, artifacts and expansions.");
			RespectExisting = ((BaseUnityPlugin)this).Config.Bind<bool>("Compatibility", "RespectExistingMods", true, "Let installed AutoSprint/EmptyChestsBeGone handle their features, and avoid adding a second QuickRestart pause button. Remove those mods to use this mod's implementations.");
		}

		private void OnEnable()
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected O, but got Unknown
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Expected O, but got Unknown
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Expected O, but got Unknown
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Expected O, but got Unknown
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Expected O, but got Unknown
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Expected O, but got Unknown
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Expected O, but got Unknown
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Expected O, but got Unknown
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Expected O, but got Unknown
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Expected O, but got Unknown
			if (!hooked)
			{
				hooked = true;
				EntityStateMachine.SetState += new hook_SetState(ObserveChestState);
				PlayerCharacterMasterController.PollButtonInput += new hook_PollButtonInput(ApplyAutoSprint);
				Run.HandlePostRunDestination += new hook_HandlePostRunDestination(PostRunDestination);
				EclipseRun.HandlePostRunDestination += new hook_HandlePostRunDestination(EclipsePostRunDestination);
				WeeklyRun.HandlePostRunDestination += new hook_HandlePostRunDestination(WeeklyPostRunDestination);
				PauseScreenController.Start += new hook_Start(AddRestartButton);
				GameEndReportPanelController.SetContinueButtonAction += new hook_SetContinueButtonAction(AddGameOverRestart);
				ShrineHealingBehavior.onActivated += ReviveAtWoods;
				PreGameRuleVoteController.SetVote += new hook_SetVote(RememberVote);
				PreGameRuleVoteController.Start += new hook_Start(InitializePreferences);
				Run.onRunStartGlobal += RememberRunSelections;
				ChatBox.SubmitChat += new hook_SubmitChat(SubmitDebugChat);
			}
		}

		private void Start()
		{
			string[] source = (from p in Chainloader.PluginInfos.Values
				where (Object)(object)p.Instance != (Object)(object)this
				select p.Metadata.Name).ToArray();
			otherChests = source.Any((string n) => n.Equals("EmptyChestsBeGone", StringComparison.OrdinalIgnoreCase));
			otherSprint = source.Any((string n) => n.Equals("AutoSprint", StringComparison.OrdinalIgnoreCase));
			otherRestart = source.Any((string n) => n.Equals("QuickRestart", StringComparison.OrdinalIgnoreCase));
			if (RespectExisting.Value && (otherChests || otherSprint || otherRestart))
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Existing feature mods detected: " + string.Join(", ", source.Where((string n) => n.Equals("EmptyChestsBeGone", StringComparison.OrdinalIgnoreCase) || n.Equals("AutoSprint", StringComparison.OrdinalIgnoreCase) || n.Equals("QuickRestart", StringComparison.OrdinalIgnoreCase))) + ". Duplicate features/buttons are suppressed. Quality of RoR2 takes over when these mods are disabled."));
			}
		}

		private void Update()
		{
			UpdatePreferences();
		}

		private void OnDisable()
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected O, but got Unknown
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Expected O, but got Unknown
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Expected O, but got Unknown
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Expected O, but got Unknown
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Expected O, but got Unknown
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Expected O, but got Unknown
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Expected O, but got Unknown
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Expected O, but got Unknown
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Expected O, but got Unknown
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Expected O, but got Unknown
			if (!hooked)
			{
				return;
			}
			hooked = false;
			EntityStateMachine.SetState -= new hook_SetState(ObserveChestState);
			PlayerCharacterMasterController.PollButtonInput -= new hook_PollButtonInput(ApplyAutoSprint);
			Run.HandlePostRunDestination -= new hook_HandlePostRunDestination(PostRunDestination);
			EclipseRun.HandlePostRunDestination -= new hook_HandlePostRunDestination(EclipsePostRunDestination);
			WeeklyRun.HandlePostRunDestination -= new hook_HandlePostRunDestination(WeeklyPostRunDestination);
			PauseScreenController.Start -= new hook_Start(AddRestartButton);
			GameEndReportPanelController.SetContinueButtonAction -= new hook_SetContinueButtonAction(AddGameOverRestart);
			ShrineHealingBehavior.onActivated -= ReviveAtWoods;
			PreGameRuleVoteController.SetVote -= new hook_SetVote(RememberVote);
			PreGameRuleVoteController.Start -= new hook_Start(InitializePreferences);
			readyPreferenceVotes.Clear();
			Run.onRunStartGlobal -= RememberRunSelections;
			ChatBox.SubmitChat -= new hook_SubmitChat(SubmitDebugChat);
			preferences?.Flush();
			((MonoBehaviour)this).StopAllCoroutines();
			restartGate.Finish();
			restartingRun = null;
			OpenedChestVisual[] array = Resources.FindObjectsOfTypeAll<OpenedChestVisual>();
			foreach (OpenedChestVisual openedChestVisual in array)
			{
				if (Object.op_Implicit((Object)(object)openedChestVisual))
				{
					Object.Destroy((Object)(object)openedChestVisual);
				}
			}
			RestartMenuButton[] array2 = Resources.FindObjectsOfTypeAll<RestartMenuButton>();
			foreach (RestartMenuButton restartMenuButton in array2)
			{
				if (Object.op_Implicit((Object)(object)restartMenuButton))
				{
					Object.Destroy((Object)(object)((Component)restartMenuButton).gameObject);
				}
			}
		}

		private void OnDestroy()
		{
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
			}
		}

		private void OnApplicationQuit()
		{
			preferences?.Flush();
		}

		internal string RequestRestart()
		{
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Expected O, but got Unknown
			if (!QuickRestart.Value)
			{
				return "Quick restart is disabled in config.";
			}
			if (!NetworkServer.active)
			{
				return "Only the host can restart the run.";
			}
			Run instance = Run.instance;
			NetworkSession instance2 = NetworkSession.instance;
			if (!Object.op_Implicit((Object)(object)instance) || !Object.op_Implicit((Object)(object)instance2))
			{
				return "Start a run first.";
			}
			if (restartGate.Pending)
			{
				return "A restart is already in progress.";
			}
			if (SceneExitController.isRunning && !instance.isGameOverServer)
			{
				return "Wait for the stage transition to finish.";
			}
			Run gameModePrefabComponent = GameModeCatalog.GetGameModePrefabComponent(instance.gameModeIndex);
			if (!Object.op_Implicit((Object)(object)gameModePrefabComponent))
			{
				return "This game mode has no restart prefab.";
			}
			Type type = ((object)instance).GetType().GetMethod("HandlePostRunDestination", BindingFlags.Instance | BindingFlags.NonPublic)?.DeclaringType;
			if (type != typeof(Run) && type != typeof(EclipseRun) && type != typeof(WeeklyRun))
			{
				return "This custom game mode needs a restart adapter. Your run was left unchanged.";
			}
			RuleBook val = new RuleBook();
			val.Copy(instance.ruleBook);
			ulong seed = (reuseSeed.Value ? instance.seed : gameModePrefabComponent.GenerateSeedForNewRun());
			if (!restartGate.TryBegin(host: true, hasRun: true, transitioning: false, supportedMode: true))
			{
				return "A restart is already in progress.";
			}
			restartingRun = instance;
			((MonoBehaviour)this).StartCoroutine(Restart(instance2, instance, gameModePrefabComponent, val, seed));
			return "Restarting with the same run settings.";
		}

		private void PostRunDestination(orig_HandlePostRunDestination orig, Run run)
		{
			if (!restartGate.Pending || run != restartingRun)
			{
				orig.Invoke(run);
			}
		}

		private void EclipsePostRunDestination(orig_HandlePostRunDestination orig, EclipseRun run)
		{
			if (!restartGate.Pending || (object)run != restartingRun)
			{
				orig.Invoke(run);
			}
		}

		private void WeeklyPostRunDestination(orig_HandlePostRunDestination orig, WeeklyRun run)
		{
			if (!restartGate.Pending || (object)run != restartingRun)
			{
				orig.Invoke(run);
			}
		}

		private IEnumerator Restart(NetworkSession session, Run oldRun, Run prefab, RuleBook rules, ulong seed)
		{
			try
			{
				PauseManager.ClosePauseScreen(true);
				NetworkServer.SetAllClientsNotReady();
				session.EndRun();
				yield return null;
				yield return null;
				float deadline = Time.unscaledTime + 10f;
				while (Object.op_Implicit((Object)(object)oldRun) && Time.unscaledTime < deadline)
				{
					yield return null;
				}
				if (!NetworkServer.active || !Object.op_Implicit((Object)(object)session) || (Object)(object)session != (Object)(object)NetworkSession.instance || Object.op_Implicit((Object)(object)oldRun) || Object.op_Implicit((Object)(object)Run.instance))
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)"Restart cancelled: session changed or the old run did not finish cleanup.");
				}
				else if (!Object.op_Implicit((Object)(object)session.BeginRun(prefab, rules, seed)))
				{
					((BaseUnityPlugin)this).Logger.LogError((object)"Native BeginRun declined the restart.");
				}
				else
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)("Quick restart: preserved game mode/rule book and each player's survivor/loadout; seed " + seed + "."));
				}
			}
			finally
			{
				QualityPlugin qualityPlugin = this;
				qualityPlugin.restartGate.Finish();
				qualityPlugin.restartingRun = null;
			}
		}

		private void AddRestartButton(orig_Start orig, PauseScreenController menu)
		{
			orig.Invoke(menu);
			if (RestartButton && Object.op_Implicit((Object)(object)Run.instance) && NetworkServer.active && Object.op_Implicit((Object)(object)menu.exitGameButton))
			{
				AddMenuRestart(menu.exitGameButton.GetComponent<MPButton>());
			}
		}

		private void AddGameOverRestart(orig_SetContinueButtonAction orig, GameEndReportPanelController report, UnityAction action)
		{
			orig.Invoke(report, action);
			if (action == null || !QuickRestart.Value || !Object.op_Implicit((Object)(object)Run.instance) || !NetworkServer.active || !Object.op_Implicit((Object)(object)report.continueButton))
			{
				return;
			}
			MPButton[] componentsInChildren = ((Component)((Component)report.continueButton).transform.parent).GetComponentsInChildren<MPButton>(true);
			foreach (MPButton val in componentsInChildren)
			{
				if ((Object)(object)val != (Object)(object)report.continueButton && ((Object)val).name.IndexOf("restart", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return;
				}
			}
			AddMenuRestart(report.continueButton);
		}

		private void AddMenuRestart(MPButton source)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Expected O, but got Unknown
			if (Object.op_Implicit((Object)(object)source) && !Object.op_Implicit((Object)(object)((Component)((Component)source).transform.parent).GetComponentInChildren<RestartMenuButton>(true)))
			{
				MPButton val = NativeMenuButton.Create(source, "Quick restart", (UnityAction)delegate
				{
					Chat.AddMessage("<color=#B5E4D4>[Quality of RoR2]</color> " + RequestRestart());
				});
				((Component)val).gameObject.AddComponent<RestartMenuButton>().Button = val;
				TooltipProvider obj = ((Component)val).gameObject.AddComponent<TooltipProvider>();
				obj.overrideTitleText = "Quick restart";
				obj.overrideBodyText = "Start again with the same survivors, loadouts, difficulty and artifacts. Host only.";
			}
		}

		private void ReviveAtWoods(ShrineHealingBehavior shrine, Interactor activator)
		{
			//IL_0292: 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_02b8: Expected O, but got Unknown
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: Unknown result type (might be due to invalid IL or missing references)
			//IL_015f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_0163: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			//IL_0177: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ab: 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_01a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01af: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_020e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0213: Unknown result type (might be due to invalid IL or missing references)
			//IL_0216: Unknown result type (might be due to invalid IL or missing references)
			//IL_0220: Unknown result type (might be due to invalid IL or missing references)
			//IL_0228: Unknown result type (might be due to invalid IL or missing references)
			//IL_022d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0238: Expected O, but got Unknown
			if (!WoodRevive.Value || !NetworkServer.active || !Object.op_Implicit((Object)(object)Run.instance) || Run.instance.isGameOverServer || SceneExitController.isRunning || !Object.op_Implicit((Object)(object)shrine) || !Object.op_Implicit((Object)(object)activator))
			{
				return;
			}
			CharacterBody component = ((Component)activator).GetComponent<CharacterBody>();
			if (!Object.op_Implicit((Object)(object)component) || !Object.op_Implicit((Object)(object)component.teamComponent))
			{
				return;
			}
			int num = 0;
			PlayerCharacterMasterController[] array = PlayerCharacterMasterController.instances.ToArray();
			RaycastHit val4 = default(RaycastHit);
			foreach (PlayerCharacterMasterController val in array)
			{
				CharacterMaster val2 = (Object.op_Implicit((Object)(object)val) ? val.master : null);
				if (!Object.op_Implicit((Object)(object)val2) || !Object.op_Implicit((Object)(object)val.networkUser) || val2.teamIndex != component.teamComponent.teamIndex || !Object.op_Implicit((Object)(object)val2.bodyPrefab))
				{
					continue;
				}
				CharacterBody body = val2.GetBody();
				if (!FeatureRules.Revive(connectedTeammate: true, Object.op_Implicit((Object)(object)body) && Object.op_Implicit((Object)(object)body.healthComponent) && body.healthComponent.alive, val2.IsExtraLifePendingServer(), val2.IsDeadAndOutOfLivesServer()))
				{
					continue;
				}
				try
				{
					float num2 = (float)num * 137.5f * (MathF.PI / 180f);
					Vector3 val3 = ((Component)shrine).transform.position + new Vector3(Mathf.Cos(num2), 0f, Mathf.Sin(num2)) * 2.5f;
					val3 = ((!Physics.Raycast(val3 + Vector3.up * 6f, Vector3.down, ref val4, 18f, LayerMask.op_Implicit(((LayerIndex)(ref LayerIndex.world)).mask), (QueryTriggerInteraction)1)) ? component.footPosition : ((RaycastHit)(ref val4)).point);
					CharacterBody val5 = val2.Respawn(val3 + Vector3.up * 0.15f, ((Component)component).transform.rotation, true);
					if (Object.op_Implicit((Object)(object)val5))
					{
						val5.AddTimedBuff(Buffs.Immune, 3f);
						GameObject val6 = LegacyResourcesAPI.Load<GameObject>("Prefabs/Effects/HippoRezEffect");
						if (Object.op_Implicit((Object)(object)val6))
						{
							EffectManager.SpawnEffect(val6, new EffectData
							{
								origin = val5.corePosition,
								rotation = ((Component)val5).transform.rotation
							}, true);
						}
						Util.PlaySound("Play_item_proc_extraLife", ((Component)val5).gameObject);
						num++;
					}
				}
				catch (Exception ex)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)("Woods revive failed for " + val.networkUser.userName + ": " + ex.Message));
				}
			}
			if (num > 0)
			{
				Chat.SendBroadcastChat((ChatMessageBase)new SimpleChatMessage
				{
					baseToken = "<color=#B5E4D4>Shrine of the Woods revived " + num + " teammate(s).</color>"
				});
			}
		}
	}
	internal static class FeatureRules
	{
		internal static bool Sprint(bool enabled, bool alive, bool controlAllowed, bool moving, bool walkHeld, bool flyingControls, bool blockingSkill)
		{
			if (enabled && alive && controlAllowed && moving && !walkHeld && !flyingControls)
			{
				return !blockingSkill;
			}
			return false;
		}

		internal static bool HideChest(bool enabled, bool opened, bool available, float elapsed, float delay)
		{
			if (enabled && opened && !available)
			{
				return elapsed >= delay;
			}
			return false;
		}

		internal static bool Revive(bool connectedTeammate, bool alive, bool extraLifePending, bool deadAndOutOfLives)
		{
			return connectedTeammate && !alive && !extraLifePending && deadAndOutOfLives;
		}
	}
	internal sealed class RestartGate
	{
		internal bool Pending { get; private set; }

		internal bool TryBegin(bool host, bool hasRun, bool transitioning, bool supportedMode)
		{
			if (Pending || !host || !hasRun || transitioning || !supportedMode)
			{
				return false;
			}
			Pending = true;
			return true;
		}

		internal void Finish()
		{
			Pending = false;
		}
	}
	internal sealed class NativeMenuButton : MonoBehaviour
	{
		private RectTransform source;

		private Vector2 oldMin;

		private Vector2 oldMax;

		private bool reserved;

		internal static MPButton Create(MPButton template, string title, UnityAction click)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Expected O, but got Unknown
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Expected O, but got Unknown
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Expected O, but got Unknown
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_0219: Unknown result type (might be due to invalid IL or missing references)
			//IL_0226: Unknown result type (might be due to invalid IL or missing references)
			//IL_0233: Unknown result type (might be due to invalid IL or missing references)
			//IL_0262: Unknown result type (might be due to invalid IL or missing references)
			//IL_0267: Unknown result type (might be due to invalid IL or missing references)
			//IL_026d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0272: Unknown result type (might be due to invalid IL or missing references)
			//IL_027a: Unknown result type (might be due to invalid IL or missing references)
			//IL_027b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0285: Unknown result type (might be due to invalid IL or missing references)
			//IL_0286: 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_029b: Unknown result type (might be due to invalid IL or missing references)
			//IL_02af: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0175: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: Unknown result type (might be due to invalid IL or missing references)
			RectTransform val = (RectTransform)((Component)template).transform;
			GameObject val2 = new GameObject("QualityOfRoR2.QuickRestart", new Type[1] { typeof(RectTransform) });
			RectTransform val3 = (RectTransform)val2.transform;
			((Transform)val3).SetParent(((Transform)val).parent, false);
			val3.anchorMin = val.anchorMin;
			val3.anchorMax = val.anchorMax;
			val3.pivot = val.pivot;
			val3.sizeDelta = val.sizeDelta;
			val3.anchoredPosition = val.anchoredPosition;
			((Transform)val3).SetSiblingIndex(((Transform)val).GetSiblingIndex());
			NativeMenuButton nativeMenuButton = val2.AddComponent<NativeMenuButton>();
			Rect rect;
			if (!Object.op_Implicit((Object)(object)((Component)((Transform)val).parent).GetComponent<LayoutGroup>()))
			{
				nativeMenuButton.source = val;
				nativeMenuButton.oldMin = val.offsetMin;
				nativeMenuButton.oldMax = val.offsetMax;
				nativeMenuButton.reserved = true;
				rect = val.rect;
				float num = ((Rect)(ref rect)).width * 0.5f + 4f;
				val3.offsetMax -= Vector2.right * num;
				val.offsetMin += Vector2.right * num;
			}
			else
			{
				LayoutElement component = ((Component)template).GetComponent<LayoutElement>();
				LayoutElement obj = val2.AddComponent<LayoutElement>();
				float preferredWidth;
				if (!Object.op_Implicit((Object)(object)component))
				{
					rect = val.rect;
					preferredWidth = ((Rect)(ref rect)).width;
				}
				else
				{
					preferredWidth = component.preferredWidth;
				}
				obj.preferredWidth = preferredWidth;
				float preferredHeight;
				if (!Object.op_Implicit((Object)(object)component) || !(component.preferredHeight > 0f))
				{
					rect = val.rect;
					preferredHeight = Mathf.Max(40f, ((Rect)(ref rect)).height);
				}
				else
				{
					preferredHeight = component.preferredHeight;
				}
				obj.preferredHeight = preferredHeight;
			}
			Image val4 = val2.AddComponent<Image>();
			if (Object.op_Implicit((Object)(object)((Selectable)template).image))
			{
				val4.sprite = ((Selectable)template).image.sprite;
				((Graphic)val4).material = ((Graphic)((Selectable)template).image).material;
				val4.type = ((Selectable)template).image.type;
				((Graphic)val4).color = ((Graphic)((Selectable)template).image).color;
				val4.pixelsPerUnitMultiplier = ((Selectable)template).image.pixelsPerUnitMultiplier;
			}
			MPButton val5 = val2.AddComponent<MPButton>();
			((Selectable)val5).targetGraphic = (Graphic)(object)val4;
			((Selectable)val5).transition = ((Selectable)template).transition;
			((Selectable)val5).colors = ((Selectable)template).colors;
			((Selectable)val5).spriteState = ((Selectable)template).spriteState;
			((UnityEvent)((Button)val5).onClick).AddListener(click);
			GameObject val6 = new GameObject("Label", new Type[1] { typeof(RectTransform) });
			RectTransform val7 = (RectTransform)val6.transform;
			((Transform)val7).SetParent((Transform)(object)val3, false);
			val7.anchorMin = Vector2.zero;
			val7.anchorMax = Vector2.one;
			val7.offsetMin = new Vector2(8f, 2f);
			val7.offsetMax = new Vector2(-8f, -2f);
			TextMeshProUGUI componentInChildren = ((Component)template).GetComponentInChildren<TextMeshProUGUI>(true);
			HGTextMeshProUGUI obj2 = val6.AddComponent<HGTextMeshProUGUI>();
			((TMP_Text)obj2).text = title;
			((TMP_Text)obj2).alignment = (TextAlignmentOptions)514;
			((TMP_Text)obj2).font = (Object.op_Implicit((Object)(object)HGTextMeshProUGUI.defaultLanguageFont) ? HGTextMeshProUGUI.defaultLanguageFont : (Object.op_Implicit((Object)(object)componentInChildren) ? ((TMP_Text)componentInChildren).font : TMP_Settings.defaultFontAsset));
			((TMP_Text)obj2).fontSize = (Object.op_Implicit((Object)(object)componentInChildren) ? ((TMP_Text)componentInChildren).fontSize : 24f);
			((TMP_Text)obj2).enableAutoSizing = true;
			((TMP_Text)obj2).fontSizeMin = 12f;
			((TMP_Text)obj2).fontSizeMax = ((TMP_Text)obj2).fontSize;
			((Graphic)obj2).raycastTarget = false;
			Transform parent = ((Transform)val).parent;
			LayoutRebuilder.MarkLayoutForRebuild((RectTransform)(object)((parent is RectTransform) ? parent : null));
			return val5;
		}

		private void OnDestroy()
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			if (reserved && Object.op_Implicit((Object)(object)source))
			{
				source.offsetMin = oldMin;
				source.offsetMax = oldMax;
			}
		}
	}
	internal sealed class OpenedChestVisual : MonoBehaviour
	{
		private EntityStateMachine machine;

		private PurchaseInteraction purchase;

		private float openedAt;

		private float nextCheck;

		private bool hidden;

		private readonly Dictionary<Renderer, bool> renderers = new Dictionary<Renderer, bool>();

		private readonly Dictionary<Animator, bool> animators = new Dictionary<Animator, bool>();

		private readonly Dictionary<Collider, bool> colliders = new Dictionary<Collider, bool>();

		private readonly Dictionary<ParticleSystem, bool> particles = new Dictionary<ParticleSystem, bool>();

		internal static bool IsOpened(EntityState state)
		{
			if (!(state is Opened))
			{
				return ((object)state)?.GetType().FullName == "RoR2.RouletteChestController+Opened";
			}
			return true;
		}

		internal void Opened(EntityStateMachine source)
		{
			machine = source;
			purchase = ((Component)this).GetComponent<PurchaseInteraction>();
			openedAt = Time.unscaledTime;
		}

		private void Update()
		{
			if (!(Time.unscaledTime < nextCheck))
			{
				nextCheck = Time.unscaledTime + 0.25f;
				QualityPlugin instance = QualityPlugin.Instance;
				bool opened = Object.op_Implicit((Object)(object)machine) && IsOpened(machine.state);
				bool available = Object.op_Implicit((Object)(object)purchase) && purchase.available;
				bool flag = FeatureRules.HideChest(Object.op_Implicit((Object)(object)instance) && ((Behaviour)instance).isActiveAndEnabled && instance.ChestFeature, opened, available, Time.unscaledTime - openedAt, Object.op_Implicit((Object)(object)instance) ? instance.ChestDelay.Value : 0.75f);
				if (flag && !hidden)
				{
					Hide();
				}
				else if (!flag && hidden)
				{
					Restore();
				}
			}
		}

		private void Hide()
		{
			hidden = true;
			Renderer[] componentsInChildren = ((Component)this).GetComponentsInChildren<Renderer>(true);
			foreach (Renderer val in componentsInChildren)
			{
				if (Object.op_Implicit((Object)(object)val))
				{
					renderers[val] = val.enabled;
					val.enabled = false;
				}
			}
			Animator[] componentsInChildren2 = ((Component)this).GetComponentsInChildren<Animator>(true);
			foreach (Animator val2 in componentsInChildren2)
			{
				if (Object.op_Implicit((Object)(object)val2))
				{
					animators[val2] = ((Behaviour)val2).enabled;
					((Behaviour)val2).enabled = false;
				}
			}
			Collider[] componentsInChildren3 = ((Component)this).GetComponentsInChildren<Collider>(true);
			foreach (Collider val3 in componentsInChildren3)
			{
				if (Object.op_Implicit((Object)(object)val3) && !Object.op_Implicit((Object)(object)((Component)val3).GetComponentInParent<GenericPickupController>()))
				{
					colliders[val3] = val3.enabled;
					val3.enabled = false;
				}
			}
			ParticleSystem[] componentsInChildren4 = ((Component)this).GetComponentsInChildren<ParticleSystem>(true);
			foreach (ParticleSystem val4 in componentsInChildren4)
			{
				if (Object.op_Implicit((Object)(object)val4))
				{
					particles[val4] = val4.isPlaying;
					val4.Stop(false, (ParticleSystemStopBehavior)0);
				}
			}
		}

		private void Restore()
		{
			hidden = false;
			foreach (KeyValuePair<Renderer, bool> renderer in renderers)
			{
				if (Object.op_Implicit((Object)(object)renderer.Key))
				{
					renderer.Key.enabled = renderer.Value;
				}
			}
			foreach (KeyValuePair<Animator, bool> animator in animators)
			{
				if (Object.op_Implicit((Object)(object)animator.Key))
				{
					((Behaviour)animator.Key).enabled = animator.Value;
				}
			}
			foreach (KeyValuePair<Collider, bool> collider in colliders)
			{
				if (Object.op_Implicit((Object)(object)collider.Key))
				{
					collider.Key.enabled = collider.Value;
				}
			}
			foreach (KeyValuePair<ParticleSystem, bool> particle in particles)
			{
				if (Object.op_Implicit((Object)(object)particle.Key) && particle.Value)
				{
					particle.Key.Play(false);
				}
			}
			renderers.Clear();
			animators.Clear();
			colliders.Clear();
			particles.Clear();
		}

		private void OnDestroy()
		{
			if (hidden)
			{
				Restore();
			}
		}
	}
	internal sealed class RestartMenuButton : MonoBehaviour
	{
		internal MPButton Button;

		private void Update()
		{
			if (Object.op_Implicit((Object)(object)Button))
			{
				((Selectable)Button).interactable = Object.op_Implicit((Object)(object)QualityPlugin.Instance) && QualityPlugin.Instance.QuickRestart.Value && NetworkServer.active && Object.op_Implicit((Object)(object)Run.instance) && (!SceneExitController.isRunning || Run.instance.isGameOverServer);
			}
		}
	}
	[Serializable]
	internal sealed class SavedChoice
	{
		public string rule;

		public string choice;
	}
	[Serializable]
	internal sealed class SavedMode
	{
		public string mode;

		public List<SavedChoice> choices = new List<SavedChoice>();
	}
	[Serializable]
	internal sealed class SelectionDocument
	{
		public int version = 2;

		public List<SavedMode> modes = new List<SavedMode>();
	}
	internal sealed class SelectionStore
	{
		internal readonly string Path;

		private bool protectedFile;

		private bool damagedPrimary;

		internal const string NeutralChoice = "@neutral";

		internal SelectionDocument Document { get; private set; }

		internal bool Dirty { get; private set; }

		internal static bool Relevant(string rule)
		{
			if (!(rule == "Difficulty"))
			{
				return rule?.StartsWith("Artifacts.", StringComparison.Ordinal) ?? false;
			}
			return true;
		}

		internal static string ProfileDirectory(string profile)
		{
			using SHA256 sHA = SHA256.Create();
			string path = BitConverter.ToString(sHA.ComputeHash(Encoding.UTF8.GetBytes(profile))).Replace("-", "").Substring(0, 24)
				.ToLowerInvariant();
			return System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Modwright", "QualityOfRoR2", path);
		}

		internal SelectionStore(string directory)
		{
			Path = System.IO.Path.Combine(directory, "RunPreferences.json");
			Document = Read(Path) ?? Read(Path + ".bak") ?? new SelectionDocument();
		}

		private SelectionDocument Read(string file)
		{
			if (!File.Exists(file))
			{
				return null;
			}
			try
			{
				string text = File.ReadAllText(file);
				SelectionDocument selectionDocument = JsonUtility.FromJson<SelectionDocument>(text);
				if (selectionDocument != null && selectionDocument.version != 1 && selectionDocument.version != 2)
				{
					protectedFile = true;
					throw new InvalidDataException("Unsupported preferences version; original file preserved.");
				}
				if (selectionDocument?.modes == null || !text.Contains("\"modes\""))
				{
					throw new InvalidDataException("Incomplete preferences document.");
				}
				selectionDocument.modes = (from m in selectionDocument.modes
					where m != null && !string.IsNullOrEmpty(m.mode)
					group m by m.mode into g
					select g.First()).ToList();
				foreach (SavedMode mode in selectionDocument.modes)
				{
					mode.choices = (from c in mode.choices ?? new List<SavedChoice>()
						where c != null && Relevant(c.rule) && !string.IsNullOrEmpty(c.choice)
						group c by c.rule into g
						select g.First()).ToList();
				}
				if (selectionDocument.version == 1)
				{
					foreach (SavedMode mode2 in selectionDocument.modes)
					{
						foreach (SavedChoice choice in mode2.choices)
						{
							if (choice.rule.StartsWith("Artifacts.", StringComparison.Ordinal) && choice.choice.EndsWith(".Off", StringComparison.Ordinal))
							{
								choice.choice = "@neutral";
							}
						}
					}
					selectionDocument.version = 2;
					Dirty = true;
				}
				return selectionDocument;
			}
			catch (Exception ex)
			{
				if (file == Path)
				{
					damagedPrimary = true;
				}
				Debug.LogWarning((object)("[QualityOfRoR2] Preferences: " + ex.Message));
				return null;
			}
		}

		internal SavedChoice[] ForMode(string mode)
		{
			return Document.modes.FirstOrDefault((SavedMode m) => m.mode == mode)?.choices.ToArray() ?? Array.Empty<SavedChoice>();
		}

		internal void Remember(string modeName, string rule, string choice)
		{
			if (!protectedFile && !string.IsNullOrEmpty(modeName) && Relevant(rule) && !string.IsNullOrEmpty(choice))
			{
				SavedMode savedMode = Document.modes.FirstOrDefault((SavedMode m) => m.mode == modeName);
				if (savedMode == null)
				{
					savedMode = new SavedMode
					{
						mode = modeName
					};
					Document.modes.Add(savedMode);
				}
				SavedChoice savedChoice = savedMode.choices.FirstOrDefault((SavedChoice c) => c.rule == rule);
				if (savedChoice == null)
				{
					savedChoice = new SavedChoice
					{
						rule = rule
					};
					savedMode.choices.Add(savedChoice);
				}
				if (!(savedChoice.choice == choice))
				{
					savedChoice.choice = choice;
					Dirty = true;
				}
			}
		}

		internal void Flush()
		{
			if (!Dirty || protectedFile)
			{
				return;
			}
			try
			{
				Directory.CreateDirectory(System.IO.Path.GetDirectoryName(Path));
				string text = Path + ".tmp";
				File.WriteAllText(text, JsonUtility.ToJson((object)Document, true), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
				if (damagedPrimary && File.Exists(Path))
				{
					File.Copy(Path, Path + ".invalid-" + DateTime.UtcNow.ToString("yyyyMMdd-HHmmss-fffffff"), overwrite: false);
				}
				if (File.Exists(Path))
				{
					File.Replace(text, Path, Path + ".bak");
				}
				else
				{
					File.Move(text, Path);
				}
				Dirty = (damagedPrimary = false);
			}
			catch (Exception ex)
			{
				Debug.LogWarning((object)("[QualityOfRoR2] Could not save preferences: " + ex.Message));
			}
		}
	}
}
namespace QualityOfRoR2.DebugCommands
{
	internal sealed class CommandContext
	{
		internal QualityPlugin Plugin;

		internal NetworkUser Caller;
	}
	internal interface ICommandModule
	{
		string Name { get; }

		string Execute(CommandContext context, string[] args);
	}
	internal sealed class StageCommands : ICommandModule
	{
		public string Name => "stage";

		internal static bool Allowed(SceneDef scene)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Invalid comparison between Unknown and I4
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Invalid comparison between Unknown and I4
			if (Object.op_Implicit((Object)(object)scene) && !scene.isOfflineScene)
			{
				if ((int)scene.sceneType != 1)
				{
					return (int)scene.sceneType == 2;
				}
				return true;
			}
			return false;
		}

		public string Execute(CommandContext context, string[] args)
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			if (args.Length != 1)
			{
				return "Usage: modwright stage <scene-name>. Use modwright stages to list names.";
			}
			if (!Object.op_Implicit((Object)(object)Run.instance) || Run.instance.isGameOverServer)
			{
				return "Start an active run first.";
			}
			if (SceneExitController.isRunning)
			{
				return "Wait for the stage transition to finish.";
			}
			SceneDef val = ((IEnumerable<SceneDef>)(object)SceneCatalog.allSceneDefs).FirstOrDefault((Func<SceneDef, bool>)((SceneDef s) => Allowed(s) && s.cachedName.Equals(args[0], StringComparison.OrdinalIgnoreCase)));
			if (!Object.op_Implicit((Object)(object)val))
			{
				return "Unknown or non-playable stage. Use modwright stages.";
			}
			if (Object.op_Implicit((Object)(object)SceneInfo.instance) && (Object)(object)SceneInfo.instance.sceneDef == (Object)(object)val)
			{
				return "Already in " + val.cachedName + ".";
			}
			Run.instance.AdvanceStage(val);
			return "Moving the whole run to " + val.cachedName + ". No gold added.";
		}
	}
	internal sealed class StageListCommands : ICommandModule
	{
		public string Name => "stages";

		public string Execute(CommandContext context, string[] args)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			if (args.Length > 1)
			{
				return "Usage: modwright stages [filter]";
			}
			string[] array = (from s in ((IEnumerable<SceneDef>)(object)SceneCatalog.allSceneDefs).Where(StageCommands.Allowed)
				select s.cachedName into n
				where args.Length == 0 || n.IndexOf(args[0], StringComparison.OrdinalIgnoreCase) >= 0
				orderby n
				select n).ToArray();
			if (array.Length != 0)
			{
				return string.Join(", ", array);
			}
			return "No matching stages.";
		}
	}
	internal sealed class PlayerCommands : ICommandModule
	{
		public string Name => "players";

		public string Execute(CommandContext context, string[] args)
		{
			if (args.Length != 0)
			{
				return "Usage: modwright players";
			}
			return string.Join("\n", NetworkUser.readOnlyInstancesList.Where((NetworkUser u) => Object.op_Implicit((Object)(object)u) && Object.op_Implicit((Object)(object)u.master)).Select(delegate(NetworkUser u)
			{
				//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)
				string[] obj = new string[7] { "#", null, null, null, null, null, null };
				NetworkInstanceId netId = ((NetworkBehaviour)u).netId;
				obj[1] = ((NetworkInstanceId)(ref netId)).Value.ToString();
				obj[2] = " ";
				obj[3] = u.userName;
				obj[4] = " · ";
				obj[5] = u.master.money.ToString("N0");
				obj[6] = " gold";
				return string.Concat(obj);
			}));
		}
	}
	internal sealed class GoldCommands : ICommandModule
	{
		public string Name => "gold";

		public string Execute(CommandContext context, string[] args)
		{
			if (args.Length < 1 || args.Length > 2 || !CommandText.Amount(args[0], out var amount))
			{
				return "Usage: modwright gold <1–1000000> [self|all|#id|\"player name\"]";
			}
			if (!Object.op_Implicit((Object)(object)Run.instance) || Run.instance.isGameOverServer)
			{
				return "Start an active run first.";
			}
			string target = ((args.Length == 2) ? args[1] : "self");
			NetworkUser[] array = NetworkUser.readOnlyInstancesList.Where((NetworkUser u) => Object.op_Implicit((Object)(object)u) && Object.op_Implicit((Object)(object)u.master)).ToArray();
			NetworkUser[] array2;
			if (target.Equals("self", StringComparison.OrdinalIgnoreCase) || target.Equals("me", StringComparison.OrdinalIgnoreCase))
			{
				array2 = array.Where((NetworkUser u) => (Object)(object)u == (Object)(object)context.Caller).ToArray();
			}
			else if (target.Equals("all", StringComparison.OrdinalIgnoreCase))
			{
				array2 = array;
			}
			else
			{
				array2 = ((!target.StartsWith("#") || !uint.TryParse(target.Substring(1), out var id)) ? array.Where((NetworkUser u) => u.userName.Equals(target, StringComparison.OrdinalIgnoreCase)).ToArray() : array.Where(delegate(NetworkUser u)
				{
					//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)
					NetworkInstanceId netId = ((NetworkBehaviour)u).netId;
					return ((NetworkInstanceId)(ref netId)).Value == id;
				}).ToArray());
			}
			if (array2.Length == 0)
			{
				return "Player not found. Use modwright players.";
			}
			if (array2.Length > 1 && !target.Equals("all", StringComparison.OrdinalIgnoreCase))
			{
				return "Player name is ambiguous. Use #id from modwright players.";
			}
			NetworkUser[] array3 = array2;
			foreach (NetworkUser val in array3)
			{
				uint num2 = Math.Min((uint)amount, uint.MaxValue - val.master.money);
				if (num2 != 0)
				{
					val.master.GiveMoney(num2, false);
				}
			}
			return "Added up to " + amount.ToString("N0") + " gold each to " + string.Join(", ", array2.Select((NetworkUser u) => u.userName)) + ".";
		}
	}
	internal sealed class RestartCommands : ICommandModule
	{
		public string Name => "restart";

		public string Execute(CommandContext context, string[] args)
		{
			if (args.Length != 0)
			{
				return "Usage: modwright restart";
			}
			return context.Plugin.RequestRestart();
		}
	}
	internal static class OwnerAccess
	{
		internal const ulong SteamId = 76561198137132060uL;

		internal static bool Allows(ulong account, bool local, bool host)
		{
			return account == 76561198137132060L && local && host;
		}
	}
	internal sealed class DebugRequest
	{
		internal string Verb;

		internal string Error;

		internal string[] Arguments = Array.Empty<string>();
	}
	internal static class CommandText
	{
		internal static bool TryParse(string text, out DebugRequest request)
		{
			request = null;
			if (string.IsNullOrWhiteSpace(text))
			{
				return false;
			}
			text = text.Trim();
			if (text.StartsWith("/"))
			{
				text = text.Substring(1);
			}
			int num = text.IndexOfAny(new char[4] { ' ', '\t', '\r', '\n' });
			if (!Known((num < 0) ? text : text.Substring(0, num)))
			{
				return false;
			}
			List<string> list = new List<string>();
			StringBuilder stringBuilder = new StringBuilder();
			char c = '\0';
			string text2 = text;
			foreach (char c2 in text2)
			{
				if (c2 == ';' || c2 == '\n' || c2 == '\r')
				{
					request = new DebugRequest
					{
						Error = "Use one modwright command at a time."
					};
					return true;
				}
				if (c != 0)
				{
					if (c2 == c)
					{
						c = '\0';
					}
					else
					{
						stringBuilder.Append(c2);
					}
				}
				else if (c2 == '"' || c2 == '\'')
				{
					c = c2;
				}
				else if (char.IsWhiteSpace(c2))
				{
					if (stringBuilder.Length > 0)
					{
						list.Add(stringBuilder.ToString());
						stringBuilder.Clear();
					}
				}
				else
				{
					stringBuilder.Append(c2);
				}
			}
			if (c != 0)
			{
				request = new DebugRequest
				{
					Error = "Unclosed player-name quote."
				};
				return true;
			}
			if (stringBuilder.Length > 0)
			{
				list.Add(stringBuilder.ToString());
			}
			request = FromArguments(list[0], list.Skip(1).ToArray());
			return true;
		}

		private static bool Known(string name)
		{
			return new string[5] { "modwright", "modwright_gold", "modwright_blueportal", "modwright_stage", "modwright_restart" }.Contains<string>(name, StringComparer.OrdinalIgnoreCase);
		}

		internal static DebugRequest FromArguments(string command, string[] args)
		{
			DebugRequest debugRequest = new DebugRequest();
			if (args.Any((string a) => a == null || a.IndexOfAny(new char[3] { ';', '\r', '\n' }) >= 0))
			{
				debugRequest.Error = "Use one modwright command at a time.";
				return debugRequest;
			}
			switch (command.ToLowerInvariant())
			{
			case "modwright_gold":
				debugRequest.Verb = "gold";
				debugRequest.Arguments = args;
				break;
			case "modwright_blueportal":
				debugRequest.Verb = "stage";
				debugRequest.Arguments = ((args.Length != 0) ? args : new string[1] { "bazaar" });
				if (args.Length != 0)
				{
					debugRequest.Error = "Usage: modwright_blueportal";
				}
				break;
			case "modwright_stage":
				debugRequest.Verb = "stage";
				debugRequest.Arguments = args;
				break;
			case "modwright_restart":
				debugRequest.Verb = "restart";
				debugRequest.Arguments = args;
				break;
			default:
				debugRequest.Verb = ((args.Length == 0) ? "help" : args[0].ToLowerInvariant());
				debugRequest.Arguments = args.Skip(1).ToArray();
				break;
			}
			if (debugRequest.Verb == "teleport" || debugRequest.Verb == "tp")
			{
				debugRequest.Verb = "stage";
			}
			if (debugRequest.Verb == "money")
			{
				debugRequest.Verb = "gold";
			}
			return debugRequest;
		}

		internal static bool Amount(string text, out int amount)
		{
			if (int.TryParse(text, NumberStyles.None, CultureInfo.InvariantCulture, out amount) && amount >= 1)
			{
				return amount <= 1000000;
			}
			return false;
		}
	}
}