using System; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Microsoft.CodeAnalysis; using RoR2; using UnityEngine; using UnityEngine.Networking; [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("PausedTimerOnTP")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.1.0.0")] [assembly: AssemblyInformationalVersion("1.1.0")] [assembly: AssemblyProduct("PausedTimerOnTP")] [assembly: AssemblyTitle("PausedTimerOnTP")] [assembly: AssemblyVersion("1.1.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 PausedTimerOnTP { public enum PauseTrigger { OnBossDefeated, OnActivation, OnChargeComplete } [BepInPlugin("Bruce.PausedTimerOnTP", "PausedTimerOnTP", "1.1.0")] public class PausedTimerOnTPPlugin : BaseUnityPlugin { public const string PluginAuthor = "Bruce"; public const string PluginName = "PausedTimerOnTP"; public const string PluginGUID = "Bruce.PausedTimerOnTP"; public const string PluginVersion = "1.1.0"; internal static ConfigEntry Enabled; internal static ConfigEntry Trigger; internal static bool TimerFrozen; private static float frozenAt; private bool patchApplied; private bool fallbackWarningLogged; private Harmony harmony; private void Awake() { //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Expected O, but got Unknown Enabled = ((BaseUnityPlugin)this).Config.Bind("General", "Enabled", true, "Master switch. When false the run timer behaves exactly like vanilla."); Trigger = ((BaseUnityPlugin)this).Config.Bind("General", "PauseTrigger", PauseTrigger.OnBossDefeated, "OnBossDefeated: pause the timer when the teleporter boss is killed.\nOnActivation: pause the timer the moment the teleporter is activated.\nOnChargeComplete: pause the timer when the teleporter reaches 100% charge."); Enabled.SettingChanged += delegate { if (!Enabled.Value) { Unfreeze("disabled in config"); } }; BossGroup.onBossGroupDefeatedServer += OnBossGroupDefeated; TeleporterInteraction.onTeleporterBeginChargingGlobal += OnTeleporterBeginCharging; TeleporterInteraction.onTeleporterChargedGlobal += OnTeleporterCharged; Stage.onStageStartGlobal += OnStageStart; Run.onRunStartGlobal += OnRunStart; harmony = new Harmony("Bruce.PausedTimerOnTP"); try { harmony.CreateClassProcessor(typeof(RunStopwatchPatch)).Patch(); patchApplied = true; } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not patch Run.ShouldUpdateRunStopwatch; using fallback clamp instead. " + ex.Message)); } ((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("{0} {1} loaded (trigger: {2}).", "PausedTimerOnTP", "1.1.0", Trigger.Value)); } private void OnDestroy() { BossGroup.onBossGroupDefeatedServer -= OnBossGroupDefeated; TeleporterInteraction.onTeleporterBeginChargingGlobal -= OnTeleporterBeginCharging; TeleporterInteraction.onTeleporterChargedGlobal -= OnTeleporterCharged; Stage.onStageStartGlobal -= OnStageStart; Run.onRunStartGlobal -= OnRunStart; Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } } private void OnBossGroupDefeated(BossGroup bossGroup) { if (Trigger.Value == PauseTrigger.OnBossDefeated && !((Object)(object)bossGroup == (Object)null) && IsTeleporterBossGroup(bossGroup)) { Freeze("teleporter boss defeated"); } } private static bool IsTeleporterBossGroup(BossGroup bossGroup) { TeleporterInteraction instance = TeleporterInteraction.instance; if ((Object)(object)instance == (Object)null) { return false; } if (!((Object)(object)instance.bossGroup == (Object)(object)bossGroup)) { return (Object)(object)((Component)bossGroup).GetComponentInParent() == (Object)(object)instance; } return true; } private void OnTeleporterBeginCharging(TeleporterInteraction teleporter) { if (Trigger.Value == PauseTrigger.OnActivation) { Freeze("teleporter activated"); } } private void OnTeleporterCharged(TeleporterInteraction teleporter) { if (Trigger.Value == PauseTrigger.OnChargeComplete) { Freeze("teleporter fully charged"); } } private void OnStageStart(Stage stage) { Unfreeze("new stage"); } private void OnRunStart(Run run) { Unfreeze("new run"); } private void Freeze(string reason) { if (NetworkServer.active && Enabled.Value && !TimerFrozen && !((Object)(object)Run.instance == (Object)null)) { frozenAt = Run.instance.GetRunStopwatch(); TimerFrozen = true; ((BaseUnityPlugin)this).Logger.LogInfo((object)$"Run timer stopped at {frozenAt:F1}s ({reason})."); } } private void Unfreeze(string reason) { if (TimerFrozen) { TimerFrozen = false; ((BaseUnityPlugin)this).Logger.LogInfo((object)("Run timer released (" + reason + ").")); } } private void FixedUpdate() { if (!TimerFrozen || !NetworkServer.active) { return; } Run instance = Run.instance; if (!((Object)(object)instance == (Object)null) && Math.Abs(instance.GetRunStopwatch() - frozenAt) > 0.05f) { instance.SetRunStopwatch(frozenAt); if (!fallbackWarningLogged) { fallbackWarningLogged = true; ((BaseUnityPlugin)this).Logger.LogWarning((object)(patchApplied ? "Stopwatch kept advancing despite the patch (a Run subclass may override it); clamping instead." : "Clamping run stopwatch via fallback.")); } } } } [HarmonyPatch(typeof(Run), "ShouldUpdateRunStopwatch")] internal static class RunStopwatchPatch { [HarmonyPostfix] private static void Postfix(ref bool __result) { if (PausedTimerOnTPPlugin.TimerFrozen) { __result = false; } } } }