Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of QualityOfRoR2 v1.1.1
plugins/QualityOfRoR2/QualityOfRoR2.dll
Decompiled 29 minutes agousing 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; } } }