using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using TMPro; using Unity.Netcode; using UnityEngine; using UnityEngine.Events; using UnityEngine.InputSystem; using UnityEngine.SceneManagement; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("QuickRestart")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+757791390981cd33951aaf2d51e0cfb4294c8a26")] [assembly: AssemblyProduct("QuickRestart")] [assembly: AssemblyTitle("QuickRestart")] [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 QuickRestart { [BepInPlugin("com.ttr.quickrestart", "QuickRestart", "3.1.89")] public class QuickRestartPlugin : BaseUnityPlugin { public const string PluginGuid = "com.ttr.quickrestart"; public const string PluginName = "QuickRestart"; public const string PluginVersion = "3.1.89"; internal static ManualLogSource Logger; private const KeyCode CancelDayKey = (KeyCode)290; private const string CancelDayBinding = "/f9"; private const float RestartTimeoutSeconds = 90f; private static QuickRestartPlugin _instance; internal static bool _isCancelling; private InputAction _cancelDayAction; private InputAction _fastForwardAction; private const string F8Binding = "/f8"; private void Awake() { _instance = this; Logger = ((BaseUnityPlugin)this).Logger; Harmony.CreateAndPatchAll(typeof(Patches), "com.ttr.quickrestart"); Logger.LogInfo((object)string.Format("{0} v{1} loaded! Press {2} or use the pause menu to restart the current run.", "QuickRestart", "3.1.89", (object)(KeyCode)290)); } private void OnDestroy() { if (_cancelDayAction != null) { _cancelDayAction.performed -= OnCancelDayPerformed; _cancelDayAction.Disable(); _cancelDayAction.Dispose(); _cancelDayAction = null; } } private static void OnCancelDayPerformed(CallbackContext _) { CancelDay(); } private static void OnF8Performed(CallbackContext _) { FastForwardToDeadline(); } internal static void EnsureCancelDayActionReady() { _instance?.EnsureCancelDayAction(); } private void EnsureCancelDayAction() { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown if (_cancelDayAction != null) { return; } try { _cancelDayAction = new InputAction("QuickRestartCancelDay", (InputActionType)1, "/f9", (string)null, (string)null, (string)null); _cancelDayAction.performed += OnCancelDayPerformed; _cancelDayAction.Enable(); Logger.LogInfo((object)"[QuickRestart] F9 restart binding ready."); } catch (Exception ex) { Logger.LogWarning((object)("[QuickRestart] F9 restart binding could not be initialized yet: " + ex.Message)); _cancelDayAction = null; } } internal static void FastForwardToDeadline() { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Invalid comparison between Unknown and I4 if (_isCancelling) { Logger.LogWarning((object)"[QuickRestart] F8 fast-forward blocked — cancellation in progress."); return; } Bootstrap val = Object.FindObjectOfType(); if ((Object)(object)val == (Object)null || (int)val.State != 1) { Logger.LogInfo((object)"[QuickRestart] F8 fast-forward skipped — not in-game."); return; } GameProgressionManager instance = GameProgressionManager.Instance; if ((Object)(object)instance == (Object)null) { Logger.LogInfo((object)"[QuickRestart] F8 fast-forward skipped — no GameProgressionManager."); return; } float num = instance.MaxSeconds - 60f; SetNetworkVar(instance, "n_currentGameTime", Mathf.Max(0f, num)); Logger.LogInfo((object)$"[QuickRestart] Fast-forwarded: maxSeconds={instance.MaxSeconds}, set to t={num:F1} (~1 real min before deadline)."); } internal static void CancelDay() { if (_isCancelling) { Logger.LogInfo((object)"[QuickRestart] Restart/cancel already in progress."); return; } if ((Object)(object)NetworkManager.Singleton == (Object)null || !NetworkManager.Singleton.IsHost) { Logger.LogInfo((object)"[QuickRestart] Only the host can cancel the day."); return; } Bootstrap val = Object.FindObjectOfType(); if ((Object)(object)val == (Object)null) { Logger.LogInfo((object)"[QuickRestart] Bootstrap not found."); return; } GameProgressionManager instance = GameProgressionManager.Instance; if ((Object)(object)instance == (Object)null) { Logger.LogWarning((object)"[QuickRestart] GameProgressionManager not found."); return; } _isCancelling = true; RunCoroutine(RestartCurrentOrLastRun(val, instance)); } private static IEnumerator RestartCurrentOrLastRun(Bootstrap bootstrap, GameProgressionManager progression) { try { Logger.LogInfo((object)$"[QuickRestart] Restart requested from {bootstrap.State} (transition: {IsInTransition()})."); bool transitionInProgress = IsInTransition(); GameProgressionManager obj = progression; bool flag = (Object)(object)((obj != null) ? obj.Deposit : null) != (Object)null && (progression.Deposit.PapyrusIsAnimating || progression.Deposit.PreventingRespawns); if (!(transitionInProgress || flag) && !Patches.AnyPlayerDead()) { Patches.HasLastGoodEquipment(); } if ((int)bootstrap.State == 1) { Logger.LogInfo((object)$"[QuickRestart] Ending current run via owned instant transition (nativeTransition={transitionInProgress}, deadline={flag}, deadPlayers={Patches.AnyPlayerDead()})."); Patches.PrepareForOwnedInstantRestart(bootstrap, progression); if (!Patches.TryStartInstantLobbyTransition(bootstrap, (GameEndedReason)0, allowActiveTransition: true)) { yield break; } } else if ((int)bootstrap.State != 0 && !transitionInProgress) { Logger.LogInfo((object)$"[QuickRestart] Cannot restart from game state {bootstrap.State}."); yield break; } yield return WaitForLobbyReady(bootstrap); bootstrap = Object.FindObjectOfType(); progression = GameProgressionManager.Instance; if ((Object)(object)bootstrap == (Object)null || (int)bootstrap.State != 0 || IsInTransition()) { if (!transitionInProgress) { Logger.LogWarning((object)"[QuickRestart] Timed out waiting for lobby/post-loss state."); yield break; } if (!IsInTransition() && (Object)(object)bootstrap != (Object)null && (int)bootstrap.State != 0) { Logger.LogInfo((object)"[QuickRestart] Native completed lobby but didn't set State; applying state."); typeof(Bootstrap).GetField("gameState", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(bootstrap, (object)(GameState)0); } else { Logger.LogInfo((object)"[QuickRestart] Native transition timed out; falling back to forced transition."); Patches.TryStartInstantLobbyTransition(bootstrap ?? Object.FindObjectOfType(), (GameEndedReason)0, allowActiveTransition: true); yield return WaitForLobbyReady(bootstrap ?? Object.FindObjectOfType()); bootstrap = Object.FindObjectOfType(); progression = GameProgressionManager.Instance; if ((Object)(object)bootstrap == (Object)null || (int)bootstrap.State != 0 || IsInTransition()) { Logger.LogWarning((object)"[QuickRestart] Forced transition also failed."); yield break; } } } Logger.LogInfo((object)"[QuickRestart] Lobby reached; cleaning up post-transition state then recovering."); Patches.CancelActiveVortex(Patches.GetCurrentGnomeHouse()); Patches.StopTaskCallbacks(); if (Patches._depositWorldSaved) { GnomeHouse currentGnomeHouse = Patches.GetCurrentGnomeHouse(); if ((Object)(object)((currentGnomeHouse != null) ? currentGnomeHouse.GnomiumDeposit : null) != (Object)null) { try { Transform transform = ((Component)currentGnomeHouse.GnomiumDeposit).transform; transform.position = Patches._savedDepositWorldPos; transform.rotation = Patches._savedDepositWorldRot; Logger.LogInfo((object)$"[QuickRestart] Restored deposit box (recovery path): worldPos={Patches._savedDepositWorldPos}"); } catch (Exception ex) { Logger.LogWarning((object)("[QuickRestart] Failed to restore deposit box (recovery path): " + ex.Message)); } } } if ((Object)(object)progression != (Object)null) { progression.SelectedLevelIndex = -1; } Patches.UseLastGoodEquipment(); ForceLobbyReady(progression); ClearTaskListAndHints(); ResetPlayerControllerUiState(); yield return Patches.RecoverLobbyPlayersAndEquipment(); try { Type type = Type.GetType("ControllerSupport.ControllerSupportPlugin, ControllerSupport"); if (type != null) { type.GetMethod("OnGameRestarted", BindingFlags.Static | BindingFlags.Public)?.Invoke(null, null); Logger.LogInfo((object)"[QuickRestart] Called ControllerSupport.OnGameRestarted."); } } catch (Exception ex2) { Logger.LogWarning((object)("[QuickRestart] ControllerSupport.OnGameRestarted failed: " + ex2.Message)); } Logger.LogInfo((object)"[QuickRestart] Restart recovery complete; staying in lobby."); } finally { FinishCancel(); } } private static IEnumerator WaitForLobbyReady(Bootstrap bootstrap, float timeoutSeconds = 90f) { float elapsed = 0f; while ((Object)(object)bootstrap != (Object)null && ((int)bootstrap.State != 0 || IsInTransition())) { elapsed += Time.unscaledDeltaTime; if (elapsed > timeoutSeconds) { break; } yield return null; } } internal static void FinishCancel() { _isCancelling = false; } internal static void RunCoroutine(IEnumerator routine) { QuickRestartPlugin instance = _instance; if (instance != null) { ((MonoBehaviour)instance).StartCoroutine(routine); } } internal static bool IsInTransition() { Bootstrap val = Object.FindObjectOfType(); if ((Object)(object)val == (Object)null) { return false; } FieldInfo field = typeof(Bootstrap).GetField("gameTransitionInProgress", BindingFlags.Instance | BindingFlags.NonPublic); if (field != null) { return (bool)field.GetValue(val); } return false; } internal static T GetPrivate(object obj, string fieldName) { FieldInfo field = obj.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic); if (field == null) { return default(T); } object value = field.GetValue(obj); if (value is T) { return (T)value; } return default(T); } internal static bool IsUnityObjectAlive(object obj) { if (obj == null) { return false; } Object val = (Object)((obj is Object) ? obj : null); if (val != null) { return val != (Object)null; } return true; } internal static void SetNetworkVar(object obj, string fieldName, T value) { if (obj.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(obj) is NetworkVariable val) { val.Value = value; } else { Logger.LogWarning((object)("[QuickRestart] Could not set " + fieldName + ".")); } } internal static void HideDeathOverlay() { //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_instance == (Object)null) { return; } string[] array = new string[6] { "DeathOverlay", "DeathScreen", "GameOver", "GameOverScreen", "ContinuePanel", "ContinueScreen" }; foreach (string text in array) { try { GameObject val = GameObject.Find(text); if ((Object)(object)val != (Object)null && val.activeInHierarchy) { Logger.LogInfo((object)("[QuickRestart] Found and hiding death overlay: " + text)); val.SetActive(false); return; } } catch { } } try { Canvas[] array2 = Object.FindObjectsOfType(); foreach (Canvas val2 in array2) { if ((Object)(object)val2 == (Object)null || !((Component)val2).gameObject.activeInHierarchy) { continue; } Scene scene = ((Component)val2).gameObject.scene; if (((Scene)(ref scene)).name == "DontDestroyOnLoad") { continue; } TMP_Text[] componentsInChildren = ((Component)val2).GetComponentsInChildren(true); foreach (TMP_Text val3 in componentsInChildren) { if (!((Object)(object)val3 == (Object)null) && !string.IsNullOrEmpty(val3.text)) { string text2 = val3.text.ToLowerInvariant(); if (text2.Contains("continue") || text2.Contains("game over") || text2.Contains("you died") || text2.Contains("death")) { Logger.LogInfo((object)("[QuickRestart] Hiding death overlay (found text: '" + val3.text + "')")); ((Component)val2).gameObject.SetActive(false); return; } } } } } catch { } Logger.LogInfo((object)"[QuickRestart] Could not find death overlay to hide."); } internal static void ClickContinueButton() { try { Button[] array = Object.FindObjectsOfType