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.Threading.Tasks; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using FacilityMeltdown.API; using FacilityMeltdown.MeltdownSequence.Behaviours; using GameNetcodeStuff; using HarmonyLib; using LethalLib.Modules; using Microsoft.CodeAnalysis; using PizzaTowerEscapeMusic; using PizzaTowerEscapeMusic.Scripting; using PizzaTowerEscapeMusic.Scripting.ScriptEvents; using TMPro; using Unity.Collections; using Unity.Netcode; using UnityEngine; using UnityEngine.Networking; using UnityEngine.TextCore; using UnityEngine.TextCore.LowLevel; 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("CombinedMeltdownPlaylistSync")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.6.0.0")] [assembly: AssemblyInformationalVersion("1.6.0+0a64f431b310f2e03a4c4184fc3ceb9955e24135")] [assembly: AssemblyProduct("CombinedMeltdownPlaylistSync")] [assembly: AssemblyTitle("CombinedMeltdownPlaylistSync")] [assembly: AssemblyVersion("1.6.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [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 CombinedMeltdownPlaylistSync { internal static class ApparatusLocatorRegistry { private const int LocatorItemId = 912042; private const string LocatorItemName = "Apparatus locator"; private static Item? _locatorItem; private static bool _registered; private static bool _removedFromStore; private static int _appliedPrice = -1; private static LocatorScanMode _appliedScanMode = (LocatorScanMode)(-1); private static int _appliedBurstCharges = -1; private static float _appliedBatterySeconds = -1f; private static float _nextRegistrationAttempt; private static bool _loggedWaitingForMapper; internal static void EnsureRegistered(StartOfRound? round = null) { if (_registered || ((Object)(object)round == (Object)null && Time.unscaledTime < _nextRegistrationAttempt)) { return; } _nextRegistrationAttempt = Time.unscaledTime + 1f; Item val = FindMapperItem(round); if ((Object)(object)val == (Object)null || (Object)(object)val.spawnPrefab == (Object)null) { if (!_loggedWaitingForMapper) { _loggedWaitingForMapper = true; Plugin.ModLog.LogDebug((object)"The vanilla Mapper item is not loaded yet; locator registration will retry when StartOfRound awakens."); } return; } try { _locatorItem = Object.Instantiate(val); ((Object)_locatorItem).name = "ApparatusLocator (Item)"; _locatorItem.itemId = 912042; _locatorItem.itemName = "Apparatus locator"; _locatorItem.isScrap = false; _locatorItem.creditsWorth = LocatorConfig.Price.Value; _locatorItem.requiresBattery = true; _locatorItem.itemIsTrigger = LocatorConfig.ScanMode.Value == LocatorScanMode.Burst; _locatorItem.batteryUsage = (_locatorItem.itemIsTrigger ? (1f / (float)LocatorConfig.BurstCharges.Value) : LocatorConfig.BatterySeconds.Value); _locatorItem.automaticallySetUsingPower = true; _locatorItem.holdButtonUse = false; _locatorItem.syncUseFunction = true; _locatorItem.saveItemVariable = false; _locatorItem.canBeGrabbedBeforeGameStart = true; _locatorItem.toolTips = new string[1] { "Toggle apparatus locator : [LMB]" }; GameObject spawnPrefab = val.spawnPrefab; MapDevice component = spawnPrefab.GetComponent(); if ((Object)(object)component == (Object)null) { throw new InvalidOperationException("The vanilla Mapper prefab no longer has a MapDevice component."); } ((GrabbableObject)component).itemProperties = _locatorItem; if ((Object)(object)spawnPrefab.GetComponent() == (Object)null) { spawnPrefab.AddComponent(); } _locatorItem.spawnPrefab = spawnPrefab; TerminalNode val2 = ScriptableObject.CreateInstance(); ((Object)val2).name = "ApparatusLocatorInfoNode"; val2.displayText = "APPARATUS LOCATOR\n\nA battery-powered locator built from an unused Mapper chassis. While switched on, it detects the docked apparatus assigned to the current facility.\n\n"; val2.clearPreviousText = true; val2.maxCharactersToType = 35; Items.RegisterShopItem(_locatorItem, (TerminalNode)null, (TerminalNode)null, val2, LocatorConfig.Price.Value); _registered = true; _loggedWaitingForMapper = false; Plugin.ModLog.LogInfo((object)("Registered the apparatus locator for " + $"{LocatorConfig.Price.Value} credits.")); } catch (Exception arg) { Plugin.ModLog.LogError((object)$"Could not register the apparatus locator: {arg}"); } } internal static void Tick() { if (!_registered) { EnsureRegistered(StartOfRound.Instance); } if ((Object)(object)_locatorItem == (Object)null) { return; } SyncedConfigSnapshot current = HostConfigSynchronizer.Current; if (_appliedScanMode != current.LocatorMode || _appliedBurstCharges != current.LocatorBurstCharges || !Mathf.Approximately(_appliedBatterySeconds, current.LocatorBatterySeconds)) { _appliedScanMode = current.LocatorMode; _appliedBurstCharges = current.LocatorBurstCharges; _appliedBatterySeconds = current.LocatorBatterySeconds; bool flag = current.LocatorMode == LocatorScanMode.Burst; _locatorItem.itemIsTrigger = flag; _locatorItem.batteryUsage = (flag ? (1f / (float)current.LocatorBurstCharges) : current.LocatorBatterySeconds); } if ((Object)(object)Items.terminal == (Object)null || HostConfigSynchronizer.WaitingForHostSnapshot) { return; } if (!current.EnableLocator) { if (!_removedFromStore) { try { Items.RemoveShopItem(_locatorItem); _removedFromStore = true; Plugin.ModLog.LogInfo((object)"Removed the apparatus locator from the store because the host disabled it."); } catch (Exception ex) { Plugin.ModLog.LogWarning((object)("Could not hide the disabled apparatus locator: " + ex.Message)); } } } else if (!_removedFromStore && _appliedPrice != current.LocatorPrice) { try { Items.UpdateShopItemPrice(_locatorItem, current.LocatorPrice); _appliedPrice = current.LocatorPrice; } catch (Exception ex2) { Plugin.ModLog.LogWarning((object)("Could not update the apparatus locator price: " + ex2.Message)); } } } internal static void Destroy() { ApparatusLocatorDisplay.DestroySharedAssets(); } internal static bool IsLocator(MapDevice device) { return string.Equals(((GrabbableObject)device).itemProperties?.itemName, "Apparatus locator", StringComparison.OrdinalIgnoreCase); } internal static ApparatusLocatorDisplay GetOrAttachDisplay(MapDevice device) { ApparatusLocatorDisplay component = ((Component)device).GetComponent(); if ((Object)(object)component != (Object)null) { return component; } component = ((Component)device).gameObject.AddComponent(); Plugin.ModLog.LogInfo((object)"Attached the apparatus-locator display to a spawned Mapper instance."); return component; } private static Item? FindMapperItem(StartOfRound? round) { Item val = ((IEnumerable)(round?.allItemsList?.itemsList?.Where((Item item) => (Object)(object)item != (Object)null).ToArray() ?? Array.Empty())).FirstOrDefault((Func)IsMapperItem); if ((Object)(object)val != (Object)null) { return val; } return ((IEnumerable)Resources.FindObjectsOfTypeAll()).FirstOrDefault((Func)IsMapperItem); } private static bool IsMapperItem(Item item) { string obj = ((Object)item).name ?? string.Empty; string a = item.itemName ?? string.Empty; GameObject spawnPrefab = item.spawnPrefab; string text = ((spawnPrefab != null) ? ((Object)spawnPrefab).name : null) ?? string.Empty; if (obj.IndexOf("MapDevice", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("MapDevice", StringComparison.OrdinalIgnoreCase) < 0) { return string.Equals(a, "Mapper", StringComparison.OrdinalIgnoreCase); } return true; } } [HarmonyPatch(typeof(StartOfRound), "Awake")] internal static class ApparatusLocatorRegistrationPatch { [HarmonyPostfix] private static void Postfix(StartOfRound __instance) { ApparatusLocatorRegistry.EnsureRegistered(__instance); } } [HarmonyPatch(typeof(MapDevice), "ItemActivate")] internal static class ApparatusLocatorActivationPatch { [HarmonyPrefix] private static bool Prefix(MapDevice __instance, bool used, bool buttonDown) { if (!ApparatusLocatorRegistry.IsLocator(__instance)) { return true; } ApparatusLocatorRegistry.GetOrAttachDisplay(__instance).OnActivated(used, buttonDown); return false; } } [HarmonyPatch(typeof(MapDevice), "Start")] internal static class ApparatusLocatorRuntimeAttachmentPatch { [HarmonyPostfix] private static void Postfix(MapDevice __instance) { if (ApparatusLocatorRegistry.IsLocator(__instance)) { ApparatusLocatorRegistry.GetOrAttachDisplay(__instance); } } } [HarmonyPatch(typeof(GrabbableObject), "UseItemOnClient")] internal static class ApparatusLocatorUseFallbackPatch { [HarmonyPostfix] private static void Postfix(GrabbableObject __instance) { MapDevice val = (MapDevice)(object)((__instance is MapDevice) ? __instance : null); if (val != null && ApparatusLocatorRegistry.IsLocator(val)) { ApparatusLocatorRegistry.GetOrAttachDisplay(val).OnActivated(((GrabbableObject)val).isBeingUsed, buttonDown: true); } } } [HarmonyPatch(typeof(MapDevice), "EquipItem")] internal static class ApparatusLocatorEquipPatch { [HarmonyPostfix] private static void Postfix(MapDevice __instance) { if (ApparatusLocatorRegistry.IsLocator(__instance) && (Object)(object)((GrabbableObject)__instance).playerHeldBy != (Object)null) { ((GrabbableObject)__instance).playerHeldBy.equippedUsableItemQE = false; } } } internal sealed class ApparatusLocatorDisplay : MonoBehaviour { private const float TargetRefreshSeconds = 0.1f; private const int DisplayWidth = 320; private const int DisplayHeight = 180; private const int DisplayLayer = 31; private static Texture2D? _arrowTexture; private static Sprite? _arrowSprite; private static AudioClip? _scanClip; private MapDevice? _device; private MeshRenderer? _modelRenderer; private Material? _originalScreenMaterial; private Material? _locatorScreenMaterial; private int _screenMaterialIndex = -1; private RenderTexture? _displayTexture; private Camera? _renderCamera; private GameObject? _renderRoot; private RectTransform? _canvasRect; private TextMeshProUGUI? _statusText; private Image? _arrowImage; private AudioSource? _scanAudioSource; private float _nextTargetRefresh; private float _nextRadarRefresh; private LungProp? _target; private bool _screenShowingLocator; private bool _hasBurstReading; private bool _modeInitialized; private LocatorScanMode _currentMode; private int _lastActivationFrame = -1; private void Awake() { _device = ((Component)this).GetComponent(); } private void OnDestroy() { RestoreOriginalScreenMaterial(); if ((Object)(object)_renderRoot != (Object)null) { Object.Destroy((Object)(object)_renderRoot); } if ((Object)(object)_locatorScreenMaterial != (Object)null) { Object.Destroy((Object)(object)_locatorScreenMaterial); } if ((Object)(object)_displayTexture != (Object)null) { _displayTexture.Release(); Object.Destroy((Object)(object)_displayTexture); } _renderRoot = null; _renderCamera = null; _displayTexture = null; _locatorScreenMaterial = null; _canvasRect = null; } internal void OnActivated(bool used, bool buttonDown) { if (Time.frameCount == _lastActivationFrame) { return; } _lastActivationFrame = Time.frameCount; SyncedConfigSnapshot current = HostConfigSynchronizer.Current; ApplyModeChange(current.LocatorMode); switch (current.LocatorMode) { case LocatorScanMode.Burst: if (buttonDown) { _hasBurstReading = true; PerformScan(current, playSound: true); } break; case LocatorScanMode.Radar: if (!used) { Hide(); break; } _nextRadarRefresh = Time.unscaledTime + current.LocatorRadarIntervalSeconds; PerformScan(current, playSound: true); break; default: if (!used) { Hide(); } else { _nextTargetRefresh = 0f; } break; } } private void LateUpdate() { if ((Object)(object)_device == (Object)null) { return; } EnsureUi(); SyncedConfigSnapshot current = HostConfigSynchronizer.Current; ApplyModeChange(current.LocatorMode); PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController; bool flag = (Object)(object)val != (Object)null && ((GrabbableObject)_device).playerHeldBy == val; bool flag2 = ((GrabbableObject)_device).insertedBattery == null || (!((GrabbableObject)_device).insertedBattery.empty && ((GrabbableObject)_device).insertedBattery.charge > 0f); if (!(current.EnableLocator && flag) || !((GrabbableObject)_device).isHeld || ((GrabbableObject)_device).isPocketed) { Hide(); return; } switch (current.LocatorMode) { case LocatorScanMode.Burst: if (!_hasBurstReading) { Hide(); } else { ShowLocatorScreen(); } break; case LocatorScanMode.Radar: if (!((GrabbableObject)_device).isBeingUsed || !flag2) { Hide(); break; } ShowLocatorScreen(); if (Time.unscaledTime >= _nextRadarRefresh) { _nextRadarRefresh = Time.unscaledTime + current.LocatorRadarIntervalSeconds; PerformScan(current, playSound: true); } break; default: if (!((GrabbableObject)_device).isBeingUsed || !flag2) { Hide(); break; } ShowLocatorScreen(); if (Time.unscaledTime >= _nextTargetRefresh) { _nextTargetRefresh = Time.unscaledTime + 0.1f; PerformScan(current, playSound: false); } break; } } private void ApplyModeChange(LocatorScanMode mode) { if (!_modeInitialized || _currentMode != mode) { _modeInitialized = true; _currentMode = mode; _hasBurstReading = false; _nextTargetRefresh = 0f; _nextRadarRefresh = 0f; Hide(); } } private void PerformScan(SyncedConfigSnapshot config, bool playSound) { PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController; if ((Object)(object)_device == (Object)null || (Object)(object)val == (Object)null || ((GrabbableObject)_device).playerHeldBy != val) { return; } EnsureUi(); if (!((Object)(object)_canvasRect == (Object)null) && !((Object)(object)_displayTexture == (Object)null) && !((Object)(object)_renderCamera == (Object)null)) { ShowLocatorScreen(); _target = FindCurrentApparatus(); UpdateReadout(val, config); if (playSound) { PlayScanSound(config.LocatorScanSoundVolume); } RenderDisplay(); } } private void EnsureUi() { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006e: 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_0082: Expected O, but got Unknown //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Expected O, but got Unknown //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: 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_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Unknown result type (might be due to invalid IL or missing references) //IL_01e2: Expected O, but got Unknown //IL_01e2: Unknown result type (might be due to invalid IL or missing references) //IL_0231: Unknown result type (might be due to invalid IL or missing references) //IL_0253: 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_027c: Unknown result type (might be due to invalid IL or missing references) //IL_028b: Unknown result type (might be due to invalid IL or missing references) //IL_02b4: Unknown result type (might be due to invalid IL or missing references) //IL_02d6: Unknown result type (might be due to invalid IL or missing references) //IL_02e5: Unknown result type (might be due to invalid IL or missing references) //IL_0319: Unknown result type (might be due to invalid IL or missing references) //IL_0328: Unknown result type (might be due to invalid IL or missing references) //IL_036e: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_canvasRect != (Object)null) && TryReplaceModelScreenMaterial()) { TMP_FontAsset orCreate = VcrFontFactory.GetOrCreate(); _renderRoot = new GameObject("Apparatus Locator Display Renderer"); ((Object)_renderRoot).hideFlags = (HideFlags)61; _displayTexture = new RenderTexture(320, 180, 16, (RenderTextureFormat)0) { name = "Apparatus Locator Display", filterMode = (FilterMode)1, wrapMode = (TextureWrapMode)1, useMipMap = false, autoGenerateMips = false, hideFlags = (HideFlags)61 }; _displayTexture.Create(); GameObject val = new GameObject("Apparatus Locator Display Camera", new Type[1] { typeof(Camera) }); ((Object)val).hideFlags = (HideFlags)61; val.layer = 31; val.transform.SetParent(_renderRoot.transform, false); _renderCamera = val.GetComponent(); ((Behaviour)_renderCamera).enabled = false; _renderCamera.clearFlags = (CameraClearFlags)2; _renderCamera.backgroundColor = new Color(0.01f, 0.025f, 0.018f, 1f); _renderCamera.orthographic = true; _renderCamera.orthographicSize = 90f; _renderCamera.nearClipPlane = 0.01f; _renderCamera.farClipPlane = 10f; _renderCamera.cullingMask = int.MinValue; _renderCamera.targetTexture = _displayTexture; GameObject val2 = new GameObject("Apparatus Locator Display Canvas", new Type[3] { typeof(RectTransform), typeof(Canvas), typeof(CanvasScaler) }) { hideFlags = (HideFlags)61, layer = 31 }; val2.transform.SetParent(_renderRoot.transform, false); RectTransform parent = (RectTransform)val2.transform; Canvas component = val2.GetComponent(); component.renderMode = (RenderMode)1; component.worldCamera = _renderCamera; component.planeDistance = 1f; component.pixelPerfect = true; component.overrideSorting = true; component.sortingOrder = 0; CanvasScaler component2 = val2.GetComponent(); component2.uiScaleMode = (ScaleMode)1; component2.referenceResolution = new Vector2(320f, 180f); component2.screenMatchMode = (ScreenMatchMode)0; component2.matchWidthOrHeight = 0f; _canvasRect = CreateRect(parent, "Locator Panel", Vector2.zero, new Vector2(320f, 180f)); Image obj = ((Component)CreateRect(_canvasRect, "Screen Background", Vector2.zero, new Vector2(320f, 180f))).gameObject.AddComponent(); ((Graphic)obj).color = new Color(0.01f, 0.025f, 0.018f, 1f); ((Graphic)obj).raycastTarget = false; _statusText = CreateText(_canvasRect, "Locator Status", orCreate, 36f, Vector2.zero, new Vector2(306f, 166f)); ((TMP_Text)_statusText).text = "LOCATOR ACTIVE"; RectTransform val3 = CreateRect(_canvasRect, "Direction Arrow", new Vector2(0f, 38f), new Vector2(72f, 72f)); _arrowImage = ((Component)val3).gameObject.AddComponent(); _arrowImage.sprite = GetArrowSprite(); ((Graphic)_arrowImage).color = new Color(0.24f, 1f, 0.42f, 1f); _arrowImage.preserveAspect = true; ((Graphic)_arrowImage).raycastTarget = false; _scanAudioSource = ((Component)this).gameObject.AddComponent(); _scanAudioSource.playOnAwake = false; _scanAudioSource.loop = false; _scanAudioSource.spatialBlend = 0.15f; Hide(); } } private void UpdateReadout(PlayerControllerB player, SyncedConfigSnapshot config) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_01fa: 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_0125: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0211: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_statusText == (Object)null || (Object)(object)_arrowImage == (Object)null) { return; } if (!player.isInsideFactory) { ShowMessage("NO APPARATUS\nDETECTED"); return; } if ((Object)(object)_target == (Object)null) { ShowMessage("NO APPARATUS\nDETECTED"); return; } float num = ConvertDistance(Vector3.Distance(((Component)player).transform.position, ((Component)_target).transform.position), config.LocatorDistanceUnits); if (config.RequireLocatorDetectionRange && num > config.LocatorDetectionRange) { ShowMessage("NO APPARATUS\nDETECTED NEARBY"); return; } bool showLocatorDistance = config.ShowLocatorDistance; bool showLocatorArrow = config.ShowLocatorArrow; ((Component)_statusText).gameObject.SetActive(showLocatorDistance || !showLocatorArrow); ((Component)_arrowImage).gameObject.SetActive(showLocatorArrow); if (showLocatorDistance) { ((TMP_Text)_statusText).rectTransform.anchoredPosition = (Vector2)(showLocatorArrow ? new Vector2(0f, -44f) : Vector2.zero); ((TMP_Text)_statusText).rectTransform.sizeDelta = (showLocatorArrow ? new Vector2(306f, 76f) : new Vector2(306f, 166f)); ((TMP_Text)_statusText).fontSize = (showLocatorArrow ? 34f : 42f); ((TMP_Text)_statusText).text = "APPARATUS " + num.ToString("F0", CultureInfo.InvariantCulture) + " " + DistanceSuffix(config.LocatorDistanceUnits); } else if (!showLocatorArrow) { ((TMP_Text)_statusText).rectTransform.anchoredPosition = Vector2.zero; ((TMP_Text)_statusText).rectTransform.sizeDelta = new Vector2(306f, 166f); ((TMP_Text)_statusText).fontSize = 40f; ((TMP_Text)_statusText).text = "APPARATUS\nDETECTED"; } if (showLocatorArrow) { ((Graphic)_arrowImage).rectTransform.anchoredPosition = (Vector2)(showLocatorDistance ? new Vector2(0f, 38f) : Vector2.zero); RotateArrowToward(player, ((Component)_target).transform.position); } } private void ShowMessage(string message) { //IL_004a: 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) if (!((Object)(object)_statusText == (Object)null) && !((Object)(object)_arrowImage == (Object)null)) { ((Component)_arrowImage).gameObject.SetActive(false); ((Component)_statusText).gameObject.SetActive(true); ((TMP_Text)_statusText).rectTransform.anchoredPosition = Vector2.zero; ((TMP_Text)_statusText).rectTransform.sizeDelta = new Vector2(306f, 166f); ((TMP_Text)_statusText).fontSize = (message.Contains("NEARBY") ? 34f : 40f); ((TMP_Text)_statusText).text = message; } } private void RotateArrowToward(PlayerControllerB player, Vector3 target) { //IL_000f: 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) //IL_001b: 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_0053: 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) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_arrowImage == (Object)null)) { Vector3 val = target - ((Component)player).transform.position; val.y = 0f; Vector3 val2 = (((Object)(object)player.gameplayCamera != (Object)null) ? ((Component)player.gameplayCamera).transform.forward : ((Component)player).transform.forward); val2.y = 0f; if (!(((Vector3)(ref val)).sqrMagnitude < 0.001f) && !(((Vector3)(ref val2)).sqrMagnitude < 0.001f)) { float num = Vector3.SignedAngle(((Vector3)(ref val2)).normalized, ((Vector3)(ref val)).normalized, Vector3.up); ((Transform)((Graphic)_arrowImage).rectTransform).localRotation = Quaternion.Euler(0f, 0f, 0f - num); } } } private void PlayScanSound(float volume) { if (!((Object)(object)_scanAudioSource == (Object)null) && !(volume <= 0f)) { _scanAudioSource.volume = Mathf.Clamp01(volume); _scanAudioSource.PlayOneShot(GetScanClip()); } } private bool TryReplaceModelScreenMaterial() { //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Expected O, but got Unknown if ((Object)(object)_locatorScreenMaterial != (Object)null) { return true; } MeshRenderer[] componentsInChildren = ((Component)this).GetComponentsInChildren(true); foreach (MeshRenderer val in componentsInChildren) { Material[] sharedMaterials = ((Renderer)val).sharedMaterials; for (int j = 0; j < sharedMaterials.Length; j++) { Material val2 = sharedMaterials[j]; if (!((Object)(object)val2 == (Object)null) && ((Object)val2).name.StartsWith("MapScreen", StringComparison.OrdinalIgnoreCase)) { _modelRenderer = val; _screenMaterialIndex = j; _originalScreenMaterial = val2; _locatorScreenMaterial = new Material(val2) { name = "Apparatus Locator Screen (Instance)", hideFlags = (HideFlags)61 }; sharedMaterials[j] = _locatorScreenMaterial; ((Renderer)val).sharedMaterials = sharedMaterials; SetScreenTexture((Texture)(object)Texture2D.blackTexture); Plugin.ModLog.LogInfo((object)($"Replaced Mapper model material slot {j} on " + "'" + ((Object)((Component)val).gameObject).name + "' with the locator display.")); return true; } } } Plugin.ModLog.LogError((object)"Could not find the Mapper model's MapScreen material; the locator display cannot be attached to its physical screen."); ((Behaviour)this).enabled = false; return false; } private void RestoreOriginalScreenMaterial() { if (!((Object)(object)_modelRenderer == (Object)null) && !((Object)(object)_originalScreenMaterial == (Object)null) && _screenMaterialIndex >= 0) { Material[] sharedMaterials = ((Renderer)_modelRenderer).sharedMaterials; if (_screenMaterialIndex < sharedMaterials.Length) { sharedMaterials[_screenMaterialIndex] = _originalScreenMaterial; ((Renderer)_modelRenderer).sharedMaterials = sharedMaterials; } } } private void ShowLocatorScreen() { if (!_screenShowingLocator && !((Object)(object)_displayTexture == (Object)null)) { SetScreenTexture((Texture)(object)_displayTexture); _screenShowingLocator = true; _nextTargetRefresh = 0f; } } private void SetScreenTexture(Texture texture) { if (!((Object)(object)_locatorScreenMaterial == (Object)null)) { _locatorScreenMaterial.mainTexture = texture; SetTextureIfPresent(_locatorScreenMaterial, "_MainTex", texture); SetTextureIfPresent(_locatorScreenMaterial, "_UnlitColorMap", texture); SetTextureIfPresent(_locatorScreenMaterial, "_BaseColorMap", texture); SetTextureIfPresent(_locatorScreenMaterial, "_EmissiveColorMap", texture); } } private static void SetTextureIfPresent(Material material, string property, Texture texture) { if (material.HasProperty(property)) { material.SetTexture(property, texture); } } private void RenderDisplay() { if (!((Object)(object)_renderCamera == (Object)null)) { Canvas.ForceUpdateCanvases(); _renderCamera.Render(); } } private static LungProp? FindCurrentApparatus() { return (from lung in Object.FindObjectsOfType() where (Object)(object)lung != (Object)null && ((Component)lung).gameObject.activeInHierarchy && lung.isLungDocked && !lung.isLungDockedInElevator && !((GrabbableObject)lung).isInShipRoom orderby ((Object)lung).GetInstanceID() select lung).FirstOrDefault(); } private static float ConvertDistance(float gameDistance, DistanceUnit unit) { if (unit != DistanceUnit.Feet) { return gameDistance; } return gameDistance * 3.28084f; } private static string DistanceSuffix(DistanceUnit unit) { return unit switch { DistanceUnit.Feet => "FT", DistanceUnit.GameUnits => "U", _ => "M", }; } private static RectTransform CreateRect(RectTransform parent, string name, Vector2 anchoredPosition, Vector2 size) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_002f: 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_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Expected O, but got Unknown RectTransform val = (RectTransform)new GameObject(name, new Type[1] { typeof(RectTransform) }) { layer = ((Component)parent).gameObject.layer }.transform; ((Transform)val).SetParent((Transform)(object)parent, false); val.anchorMin = new Vector2(0.5f, 0.5f); val.anchorMax = new Vector2(0.5f, 0.5f); val.pivot = new Vector2(0.5f, 0.5f); val.anchoredPosition = anchoredPosition; val.sizeDelta = size; return val; } private static TextMeshProUGUI CreateText(RectTransform parent, string name, TMP_FontAsset font, float size, Vector2 position, Vector2 dimensions) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) TextMeshProUGUI obj = ((Component)CreateRect(parent, name, position, dimensions)).gameObject.AddComponent(); ((TMP_Text)obj).font = font; ((TMP_Text)obj).fontSize = size; ((TMP_Text)obj).alignment = (TextAlignmentOptions)514; ((TMP_Text)obj).enableWordWrapping = true; ((TMP_Text)obj).overflowMode = (TextOverflowModes)1; ((Graphic)obj).color = new Color(0.24f, 1f, 0.42f, 1f); ((TMP_Text)obj).outlineWidth = 0.1f; ((TMP_Text)obj).outlineColor = new Color32((byte)0, (byte)20, (byte)6, (byte)220); ((Graphic)obj).raycastTarget = false; return obj; } private static Sprite GetArrowSprite() { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Expected O, but got Unknown //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_arrowSprite != (Object)null) { return _arrowSprite; } _arrowTexture = new Texture2D(64, 64, (TextureFormat)4, false) { name = "Apparatus Locator Arrow", filterMode = (FilterMode)1, wrapMode = (TextureWrapMode)1, hideFlags = (HideFlags)61 }; Color[] array = (Color[])(object)new Color[4096]; for (int i = 0; i < 64; i++) { for (int j = 0; j < 64; j++) { bool flag = j >= 28 && j <= 35 && i >= 12 && i <= 43; int num = Mathf.Max(0, 58 - i); bool flag2 = i >= 38 && i <= 58 && Mathf.Abs(j - 31) <= num; array[i * 64 + j] = ((flag || flag2) ? Color.white : Color.clear); } } _arrowTexture.SetPixels(array); _arrowTexture.Apply(false, true); _arrowSprite = Sprite.Create(_arrowTexture, new Rect(0f, 0f, 64f, 64f), new Vector2(0.5f, 0.5f), 64f); ((Object)_arrowSprite).name = "Apparatus Locator Arrow"; ((Object)_arrowSprite).hideFlags = (HideFlags)61; return _arrowSprite; } private static AudioClip GetScanClip() { if ((Object)(object)_scanClip != (Object)null) { return _scanClip; } int num = Mathf.CeilToInt(14112f); float[] array = new float[num]; for (int i = 0; i < num; i++) { float num2 = (float)i / 44100f; float num3 = num2 / 0.32f; float num4 = MathF.PI * 2f * (440f * num2 + 700f * num2 * num2 / 0.32f); float num5 = Mathf.Exp(-7f * num3) * Mathf.SmoothStep(0f, 1f, Mathf.Min(1f, num3 * 30f)); float num6 = Mathf.Sin(num4); float num7 = Mathf.Sin(num4 * 2.01f) * 0.22f; array[i] = (num6 + num7) * num5 * 0.55f; } _scanClip = AudioClip.Create("Apparatus Locator Scan", num, 1, 44100, false); _scanClip.SetData(array, 0); return _scanClip; } internal static void DestroySharedAssets() { if ((Object)(object)_arrowSprite != (Object)null) { Object.Destroy((Object)(object)_arrowSprite); } if ((Object)(object)_arrowTexture != (Object)null) { Object.Destroy((Object)(object)_arrowTexture); } if ((Object)(object)_scanClip != (Object)null) { Object.Destroy((Object)(object)_scanClip); } _arrowSprite = null; _arrowTexture = null; _scanClip = null; } private void Hide() { if (_screenShowingLocator) { SetScreenTexture((Texture)(object)Texture2D.blackTexture); _screenShowingLocator = false; } } } internal static class EscapeSpeedConfig { internal static ConfigEntry EnableDistanceBoost { get; private set; } internal static ConfigEntry FirstThreshold { get; private set; } internal static ConfigEntry FirstBoostPercent { get; private set; } internal static ConfigEntry SecondThreshold { get; private set; } internal static ConfigEntry SecondBoostPercent { get; private set; } internal static void Bind(ConfigFile config) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Expected O, but got Unknown //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Expected O, but got Unknown //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Expected O, but got Unknown //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Expected O, but got Unknown EnableDistanceBoost = config.Bind("Meltdown Escape Speed", "EnableDistanceBoost", true, "Add a movement-speed bonus while outside and far from the ship door during an active meltdown."); FirstThreshold = config.Bind("Meltdown Escape Speed", "FirstDistance", 100f, new ConfigDescription("Distance from the ship door where the first speed bonus begins.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 5000f), Array.Empty())); FirstBoostPercent = config.Bind("Meltdown Escape Speed", "FirstSpeedPercent", 5f, new ConfigDescription("Additive percentage speed bonus at FirstDistance.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 500f), Array.Empty())); SecondThreshold = config.Bind("Meltdown Escape Speed", "SecondDistance", 150f, new ConfigDescription("Distance from the ship door where the larger speed bonus begins.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 5000f), Array.Empty())); SecondBoostPercent = config.Bind("Meltdown Escape Speed", "SecondSpeedPercent", 10f, new ConfigDescription("Additive percentage speed bonus at SecondDistance. This replaces the first tier rather than stacking with it.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 500f), Array.Empty())); } } [Serializable] internal sealed class SyncedConfigSnapshot { public float ExtraStaminaPercent; public float ExtraStaminaRegenPercent; public bool PlaylistMode; public float MusicVolume; public float DepartureFadeSeconds; public bool EnableDistanceSpeedBoost; public float FirstSpeedDistance; public float FirstSpeedPercent; public float SecondSpeedDistance; public float SecondSpeedPercent; public bool EnableHud; public float HudScale; public float HudOpacity; public string BaseColor = "#FF2020"; public string FlashColor = "#FFD21A"; public bool ShowReactorTimer; public bool ShowHundredths; public bool ShowTimerPanel; public float TimerPanelOpacity; public bool ShowDistance; public bool ShowSelectedTrack; public bool ShowTrackAuthor; public bool ShowDisc; public DistanceUnit DistanceUnits; public int DistanceDecimals; public float DiscSpinSpeed; public bool EnableFlashing; public bool UseTrackBpm; public HudBeatCadence BeatCadence; public float FixedFlashIntervalSeconds; public float FlashFadeSeconds; public float TimerTransitionSpeed; public float OutsideTimerOffsetX; public float OutsideTimerOffsetY; public float InsideTimerOffsetX; public float InsideTimerOffsetY; public float SpectatorTimerOffsetX; public float SpectatorTimerOffsetY; public float DistanceOffsetX; public float DistanceOffsetY; public float TrackOffsetX; public float TrackOffsetY; public bool EnableLocator; public int LocatorPrice; public LocatorScanMode LocatorMode; public int LocatorBurstCharges; public float LocatorBatterySeconds; public float LocatorRadarIntervalSeconds; public float LocatorScanSoundVolume; public bool ShowLocatorDistance; public bool ShowLocatorArrow; public DistanceUnit LocatorDistanceUnits; public bool RequireLocatorDetectionRange; public float LocatorDetectionRange; internal static SyncedConfigSnapshot CaptureLocal() { return new SyncedConfigSnapshot { ExtraStaminaPercent = Plugin.ExtraStaminaPercent.Value, ExtraStaminaRegenPercent = Plugin.ExtraStaminaRegenPercent.Value, PlaylistMode = Plugin.PlaylistMode.Value, MusicVolume = Plugin.MusicVolume.Value, DepartureFadeSeconds = Plugin.DepartureFadeSeconds.Value, EnableDistanceSpeedBoost = EscapeSpeedConfig.EnableDistanceBoost.Value, FirstSpeedDistance = EscapeSpeedConfig.FirstThreshold.Value, FirstSpeedPercent = EscapeSpeedConfig.FirstBoostPercent.Value, SecondSpeedDistance = EscapeSpeedConfig.SecondThreshold.Value, SecondSpeedPercent = EscapeSpeedConfig.SecondBoostPercent.Value, EnableHud = HudConfig.EnableHud.Value, HudScale = 0.8f + HudConfig.HudScaleAdjustment.Value, HudOpacity = HudConfig.HudOpacity.Value, BaseColor = HudConfig.BaseColor.Value, FlashColor = HudConfig.FlashColor.Value, ShowReactorTimer = HudConfig.ShowReactorTimer.Value, ShowHundredths = HudConfig.ShowHundredths.Value, ShowTimerPanel = HudConfig.ShowTimerPanel.Value, TimerPanelOpacity = HudConfig.TimerPanelOpacity.Value, ShowDistance = HudConfig.ShowDistance.Value, ShowSelectedTrack = HudConfig.ShowSelectedTrack.Value, ShowTrackAuthor = HudConfig.ShowTrackAuthor.Value, ShowDisc = HudConfig.ShowDisc.Value, DistanceUnits = HudConfig.DistanceUnits.Value, DistanceDecimals = HudConfig.DistanceDecimals.Value, DiscSpinSpeed = HudConfig.DiscSpinSpeed.Value, EnableFlashing = HudConfig.EnableFlashing.Value, UseTrackBpm = HudConfig.UseTrackBpm.Value, BeatCadence = HudConfig.BeatCadence.Value, FixedFlashIntervalSeconds = HudConfig.FixedFlashIntervalSeconds.Value, FlashFadeSeconds = HudConfig.FlashFadeSeconds.Value, TimerTransitionSpeed = HudConfig.TimerTransitionSpeed.Value, OutsideTimerOffsetX = HudConfig.OutsideTimerOffsetX.Value, OutsideTimerOffsetY = HudConfig.OutsideTimerOffsetY.Value, InsideTimerOffsetX = HudConfig.InsideTimerOffsetX.Value, InsideTimerOffsetY = HudConfig.InsideTimerOffsetY.Value, SpectatorTimerOffsetX = HudConfig.SpectatorTimerOffsetX.Value, SpectatorTimerOffsetY = HudConfig.SpectatorTimerOffsetY.Value, DistanceOffsetX = HudConfig.DistanceOffsetX.Value, DistanceOffsetY = HudConfig.DistanceOffsetY.Value, TrackOffsetX = HudConfig.TrackOffsetX.Value, TrackOffsetY = HudConfig.TrackOffsetY.Value, EnableLocator = LocatorConfig.EnableLocator.Value, LocatorPrice = LocatorConfig.Price.Value, LocatorMode = LocatorConfig.ScanMode.Value, LocatorBurstCharges = LocatorConfig.BurstCharges.Value, LocatorBatterySeconds = LocatorConfig.BatterySeconds.Value, LocatorRadarIntervalSeconds = LocatorConfig.RadarIntervalSeconds.Value, LocatorScanSoundVolume = LocatorConfig.ScanSoundVolume.Value, ShowLocatorDistance = LocatorConfig.ShowDistance.Value, ShowLocatorArrow = LocatorConfig.ShowArrow.Value, LocatorDistanceUnits = LocatorConfig.DistanceUnits.Value, RequireLocatorDetectionRange = LocatorConfig.RequireDetectionRange.Value, LocatorDetectionRange = LocatorConfig.DetectionRange.Value }; } internal void Validate() { ExtraStaminaPercent = Mathf.Clamp(ExtraStaminaPercent, 0f, 1000f); ExtraStaminaRegenPercent = Mathf.Clamp(ExtraStaminaRegenPercent, 0f, 1000f); MusicVolume = Mathf.Clamp01(MusicVolume); DepartureFadeSeconds = Mathf.Clamp(DepartureFadeSeconds, 0f, 10f); FirstSpeedDistance = Mathf.Clamp(FirstSpeedDistance, 0f, 5000f); FirstSpeedPercent = Mathf.Clamp(FirstSpeedPercent, 0f, 500f); SecondSpeedDistance = Mathf.Clamp(SecondSpeedDistance, 0f, 5000f); SecondSpeedPercent = Mathf.Clamp(SecondSpeedPercent, 0f, 500f); HudScale = Mathf.Clamp(HudScale, 0.5f, 2.5f); HudOpacity = Mathf.Clamp01(HudOpacity); TimerPanelOpacity = Mathf.Clamp01(TimerPanelOpacity); DistanceDecimals = Mathf.Clamp(DistanceDecimals, 0, 2); DiscSpinSpeed = Mathf.Clamp(DiscSpinSpeed, -720f, 720f); FixedFlashIntervalSeconds = Mathf.Clamp(FixedFlashIntervalSeconds, 0.25f, 30f); FlashFadeSeconds = Mathf.Clamp(FlashFadeSeconds, 0.05f, 5f); TimerTransitionSpeed = Mathf.Clamp(TimerTransitionSpeed, 0.5f, 30f); LocatorPrice = Mathf.Clamp(LocatorPrice, 0, 10000); LocatorBurstCharges = Mathf.Clamp(LocatorBurstCharges, 1, 100); LocatorBatterySeconds = Mathf.Clamp(LocatorBatterySeconds, 5f, 3600f); LocatorRadarIntervalSeconds = Mathf.Clamp(LocatorRadarIntervalSeconds, 0.25f, 60f); LocatorScanSoundVolume = Mathf.Clamp01(LocatorScanSoundVolume); LocatorDetectionRange = Mathf.Clamp(LocatorDetectionRange, 1f, 5000f); if (!Enum.IsDefined(typeof(DistanceUnit), DistanceUnits)) { DistanceUnits = DistanceUnit.Meters; } if (!Enum.IsDefined(typeof(HudBeatCadence), BeatCadence)) { BeatCadence = HudBeatCadence.EveryFourBeats; } if (!Enum.IsDefined(typeof(DistanceUnit), LocatorDistanceUnits)) { LocatorDistanceUnits = DistanceUnit.Meters; } if (!Enum.IsDefined(typeof(LocatorScanMode), LocatorMode)) { LocatorMode = LocatorScanMode.Burst; } if (BaseColor == null) { BaseColor = "#FF2020"; } if (FlashColor == null) { FlashColor = "#FFD21A"; } } } internal static class HostConfigSynchronizer { private const float RefreshIntervalSeconds = 0.5f; private static SyncedConfigSnapshot? _local; private static SyncedConfigSnapshot? _host; private static string? _lastSentJson; private static int _lastClientCount = -1; private static float _nextRefreshTime; internal static bool WaitingForHostSnapshot { get { NetworkManager registeredNetworkManager = Plugin.RegisteredNetworkManager; if ((Object)(object)registeredNetworkManager != (Object)null && registeredNetworkManager.IsListening && !registeredNetworkManager.IsServer) { return _host == null; } return false; } } internal static SyncedConfigSnapshot Current { get { NetworkManager registeredNetworkManager = Plugin.RegisteredNetworkManager; if ((Object)(object)registeredNetworkManager != (Object)null && registeredNetworkManager.IsListening && !registeredNetworkManager.IsServer && _host != null) { return _host; } return _local ?? (_local = CaptureValidatedLocal()); } } internal static void Tick() { if (Time.unscaledTime < _nextRefreshTime) { return; } _nextRefreshTime = Time.unscaledTime + 0.5f; _local = CaptureValidatedLocal(); NetworkManager registeredNetworkManager = Plugin.RegisteredNetworkManager; if (!((Object)(object)registeredNetworkManager == (Object)null) && registeredNetworkManager.IsListening && registeredNetworkManager.IsServer) { string text = JsonUtility.ToJson((object)_local); int count = registeredNetworkManager.ConnectedClientsIds.Count; if (!string.Equals(text, _lastSentJson, StringComparison.Ordinal) || count != _lastClientCount) { Broadcast(text); _lastSentJson = text; _lastClientCount = count; } } } internal static void ForceBroadcast() { _local = CaptureValidatedLocal(); NetworkManager registeredNetworkManager = Plugin.RegisteredNetworkManager; if (!((Object)(object)registeredNetworkManager == (Object)null) && registeredNetworkManager.IsListening && registeredNetworkManager.IsServer) { string text = JsonUtility.ToJson((object)_local); Broadcast(text); _lastSentJson = text; _lastClientCount = registeredNetworkManager.ConnectedClientsIds.Count; } } internal static void Receive(ulong senderClientId, FastBufferReader reader) { NetworkManager registeredNetworkManager = Plugin.RegisteredNetworkManager; if ((Object)(object)registeredNetworkManager == (Object)null || registeredNetworkManager.IsServer) { return; } if (senderClientId != 0L) { Plugin.ModLog.LogWarning((object)$"Ignored configuration from non-host client {senderClientId}."); return; } try { string text = default(string); ((FastBufferReader)(ref reader)).ReadValueSafe(ref text, true); SyncedConfigSnapshot obj = JsonUtility.FromJson(text) ?? throw new InvalidOperationException("The host snapshot was empty."); obj.Validate(); _host = obj; Plugin.ModLog.LogInfo((object)"Applied the host's music, stamina, escape-speed, HUD, and apparatus-locator configuration for this lobby."); } catch (Exception arg) { Plugin.ModLog.LogError((object)$"Could not read the host's configuration: {arg}"); } } internal static void Reset() { _local = CaptureValidatedLocal(); _host = null; _lastSentJson = null; _lastClientCount = -1; _nextRefreshTime = 0f; } private static SyncedConfigSnapshot CaptureValidatedLocal() { SyncedConfigSnapshot syncedConfigSnapshot = SyncedConfigSnapshot.CaptureLocal(); syncedConfigSnapshot.Validate(); return syncedConfigSnapshot; } private unsafe static void Broadcast(string json) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) NetworkManager registeredNetworkManager = Plugin.RegisteredNetworkManager; if ((Object)(object)registeredNetworkManager == (Object)null) { return; } try { FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(4096, (Allocator)2, -1); try { ((FastBufferWriter)(ref val)).WriteValueSafe(json, true); registeredNetworkManager.CustomMessagingManager.SendNamedMessageToAll("CombinedMeltdownPlaylist.Config.v2", val, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } } catch (Exception arg) { Plugin.ModLog.LogError((object)$"Could not broadcast the host's configuration: {arg}"); } } } internal enum DistanceUnit { Meters, Feet, GameUnits } internal enum HudBeatCadence { EveryBeat, EveryFourBeats } internal static class HudConfig { internal const float BaseScale = 0.8f; internal static ConfigEntry EnableHud { get; private set; } internal static ConfigEntry HudScaleAdjustment { get; private set; } internal static ConfigEntry HudOpacity { get; private set; } internal static ConfigEntry BaseColor { get; private set; } internal static ConfigEntry FlashColor { get; private set; } internal static ConfigEntry ShowReactorTimer { get; private set; } internal static ConfigEntry ShowHundredths { get; private set; } internal static ConfigEntry ShowTimerPanel { get; private set; } internal static ConfigEntry TimerPanelOpacity { get; private set; } internal static ConfigEntry ShowDistance { get; private set; } internal static ConfigEntry ShowSelectedTrack { get; private set; } internal static ConfigEntry ShowTrackAuthor { get; private set; } internal static ConfigEntry ShowDisc { get; private set; } internal static ConfigEntry DistanceUnits { get; private set; } internal static ConfigEntry DistanceDecimals { get; private set; } internal static ConfigEntry DiscSpinSpeed { get; private set; } internal static ConfigEntry EnableFlashing { get; private set; } internal static ConfigEntry UseTrackBpm { get; private set; } internal static ConfigEntry BeatCadence { get; private set; } internal static ConfigEntry FixedFlashIntervalSeconds { get; private set; } internal static ConfigEntry FlashFadeSeconds { get; private set; } internal static ConfigEntry TimerTransitionSpeed { get; private set; } internal static ConfigEntry OutsideTimerOffsetX { get; private set; } internal static ConfigEntry OutsideTimerOffsetY { get; private set; } internal static ConfigEntry InsideTimerOffsetX { get; private set; } internal static ConfigEntry InsideTimerOffsetY { get; private set; } internal static ConfigEntry SpectatorTimerOffsetX { get; private set; } internal static ConfigEntry SpectatorTimerOffsetY { get; private set; } internal static ConfigEntry DistanceOffsetX { get; private set; } internal static ConfigEntry DistanceOffsetY { get; private set; } internal static ConfigEntry TrackOffsetX { get; private set; } internal static ConfigEntry TrackOffsetY { get; private set; } internal static void Bind(ConfigFile config) { //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Expected O, but got Unknown //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Expected O, but got Unknown //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Expected O, but got Unknown //IL_020a: Unknown result type (might be due to invalid IL or missing references) //IL_0214: Expected O, but got Unknown //IL_0242: Unknown result type (might be due to invalid IL or missing references) //IL_024c: Expected O, but got Unknown //IL_02cb: Unknown result type (might be due to invalid IL or missing references) //IL_02d5: Expected O, but got Unknown //IL_0303: Unknown result type (might be due to invalid IL or missing references) //IL_030d: Expected O, but got Unknown //IL_033b: Unknown result type (might be due to invalid IL or missing references) //IL_0345: Expected O, but got Unknown AcceptableValueRange val = new AcceptableValueRange(-0.3f, 1.7f); AcceptableValueRange val2 = new AcceptableValueRange(0.1f, 1f); AcceptableValueRange range = new AcceptableValueRange(-2000f, 2000f); EnableHud = config.Bind("Meltdown HUD - General", "EnableHud", true, "Enable all custom meltdown HUD elements."); HudScaleAdjustment = config.Bind("Meltdown HUD - General", "HudScaleAdjustment", 0f, new ConfigDescription("Adjustment added to the natural 0.8 HUD scale. Leave at 0 for the recommended size.", (AcceptableValueBase)(object)val, Array.Empty())); HudOpacity = config.Bind("Meltdown HUD - General", "HudOpacity", 1f, new ConfigDescription("Global opacity for every custom HUD element.", (AcceptableValueBase)(object)val2, Array.Empty())); BaseColor = config.Bind("Meltdown HUD - General", "BaseColor", "#FF2020", "Normal HUD color in HTML hex format."); FlashColor = config.Bind("Meltdown HUD - General", "FlashColor", "#FFD21A", "Beat-flash HUD color in HTML hex format."); ShowReactorTimer = config.Bind("Meltdown HUD - Elements", "ShowReactorTimer", true, "Show the CRITICAL FAILURE countdown."); ShowHundredths = config.Bind("Meltdown HUD - Elements", "ShowHundredths", true, "Show small hundredths next to the large total-seconds countdown."); ShowTimerPanel = config.Bind("Meltdown HUD - Elements", "ShowTimerPanel", true, "Show the rounded translucent reactor enclosure and separator line."); TimerPanelOpacity = config.Bind("Meltdown HUD - Elements", "TimerPanelOpacity", 0.32f, new ConfigDescription("Opacity of the rounded reactor panel background.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); ShowDistance = config.Bind("Meltdown HUD - Elements", "ShowDistance", true, "Show distance to the ship outside and main entrance inside."); ShowSelectedTrack = config.Bind("Meltdown HUD - Elements", "ShowSelectedTrack", true, "Show the selected track on the right side."); ShowTrackAuthor = config.Bind("Meltdown HUD - Elements", "ShowTrackAuthor", true, "Show confirmed track authors when available."); ShowDisc = config.Bind("Meltdown HUD - Elements", "ShowDisc", true, "Show the spinning transparent music disc."); DistanceUnits = config.Bind("Meltdown HUD - Elements", "DistanceUnits", DistanceUnit.Meters, "Distance units: Meters, Feet, or GameUnits."); DistanceDecimals = config.Bind("Meltdown HUD - Elements", "DistanceDecimals", 0, new ConfigDescription("Number of decimal places in distance values.", (AcceptableValueBase)(object)new AcceptableValueRange(0, 2), Array.Empty())); DiscSpinSpeed = config.Bind("Meltdown HUD - Elements", "DiscSpinSpeed", 90f, new ConfigDescription("Disc rotation speed in degrees per second. Negative values reverse it.", (AcceptableValueBase)(object)new AcceptableValueRange(-720f, 720f), Array.Empty())); EnableFlashing = config.Bind("Meltdown HUD - Flash", "EnableFlashing", true, "Enable red-to-yellow HUD pulses."); UseTrackBpm = config.Bind("Meltdown HUD - Flash", "UseTrackBpm", true, "Use the current track BPM. If disabled, use FixedFlashIntervalSeconds."); BeatCadence = config.Bind("Meltdown HUD - Flash", "BeatCadence", HudBeatCadence.EveryFourBeats, "Pulse on EveryBeat or EveryFourBeats."); FixedFlashIntervalSeconds = config.Bind("Meltdown HUD - Flash", "FixedFlashIntervalSeconds", 4f, new ConfigDescription("Pulse interval when BPM sync is disabled or metadata is unavailable.", (AcceptableValueBase)(object)new AcceptableValueRange(0.25f, 30f), Array.Empty())); FlashFadeSeconds = config.Bind("Meltdown HUD - Flash", "FlashFadeSeconds", 0.45f, new ConfigDescription("Seconds for yellow to fade back to red. It is shortened automatically when needed so every pulse has red resting time.", (AcceptableValueBase)(object)new AcceptableValueRange(0.05f, 5f), Array.Empty())); TimerTransitionSpeed = config.Bind("Meltdown HUD - Layout", "TimerTransitionSpeed", 8f, new ConfigDescription("How quickly the reactor panel glides between its inside, outside, ship, and spectator positions. Higher values move faster.", (AcceptableValueBase)(object)new AcceptableValueRange(0.5f, 30f), Array.Empty())); OutsideTimerOffsetX = BindOffset(config, "OutsideTimerOffsetX", "Horizontal adjustment for the timer beside the outside clock.", range); OutsideTimerOffsetY = BindOffset(config, "OutsideTimerOffsetY", "Vertical adjustment for the timer beside the outside clock.", range); InsideTimerOffsetX = BindOffset(config, "InsideTimerOffsetX", "Horizontal adjustment for the inside top-center timer.", range); InsideTimerOffsetY = BindOffset(config, "InsideTimerOffsetY", "Vertical adjustment for the inside top-center timer.", range); SpectatorTimerOffsetX = BindOffset(config, "SpectatorTimerOffsetX", "Horizontal adjustment for the spectator bottom-left timer.", range); SpectatorTimerOffsetY = BindOffset(config, "SpectatorTimerOffsetY", "Vertical adjustment for the spectator bottom-left timer.", range); DistanceOffsetX = BindOffset(config, "DistanceOffsetX", "Horizontal adjustment for the bottom-left distance.", range); DistanceOffsetY = BindOffset(config, "DistanceOffsetY", "Vertical adjustment for the lowered bottom-left distance.", range); TrackOffsetX = BindOffset(config, "TrackOffsetX", "Horizontal adjustment for the right-center track panel.", range); TrackOffsetY = BindOffset(config, "TrackOffsetY", "Vertical adjustment for the right-center track panel.", range); } private static ConfigEntry BindOffset(ConfigFile config, string key, string description, AcceptableValueRange range) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown return config.Bind("Meltdown HUD - Layout", key, 0f, new ConfigDescription(description, (AcceptableValueBase)(object)range, Array.Empty())); } } internal enum LocatorScanMode { Burst, Continuous, Radar } internal static class LocatorConfig { internal static ConfigEntry EnableLocator { get; private set; } internal static ConfigEntry Price { get; private set; } internal static ConfigEntry ScanMode { get; private set; } internal static ConfigEntry BurstCharges { get; private set; } internal static ConfigEntry BatterySeconds { get; private set; } internal static ConfigEntry RadarIntervalSeconds { get; private set; } internal static ConfigEntry ScanSoundVolume { get; private set; } internal static ConfigEntry ShowDistance { get; private set; } internal static ConfigEntry ShowArrow { get; private set; } internal static ConfigEntry DistanceUnits { get; private set; } internal static ConfigEntry RequireDetectionRange { get; private set; } internal static ConfigEntry DetectionRange { get; private set; } internal static void Bind(ConfigFile config) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Expected O, but got Unknown //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Expected O, but got Unknown //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Expected O, but got Unknown //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Expected O, but got Unknown //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Expected O, but got Unknown //IL_01d4: Unknown result type (might be due to invalid IL or missing references) //IL_01de: Expected O, but got Unknown EnableLocator = config.Bind("Apparatus Locator", "EnableLocator", true, "Make the apparatus locator available in the terminal store. Changing this setting requires a game restart."); Price = config.Bind("Apparatus Locator", "Price", 350, new ConfigDescription("Terminal store price in credits.", (AcceptableValueBase)(object)new AcceptableValueRange(0, 10000), Array.Empty())); ScanMode = config.Bind("Apparatus Locator", "ScanMode", LocatorScanMode.Burst, "Burst consumes one battery charge per left-click and freezes each reading. Continuous updates constantly while switched on. Radar updates at RadarIntervalSeconds while switched on."); BurstCharges = config.Bind("Apparatus Locator", "BurstCharges", 8, new ConfigDescription("Number of scans represented by a full battery in Burst mode.", (AcceptableValueBase)(object)new AcceptableValueRange(1, 100), Array.Empty())); BatterySeconds = config.Bind("Apparatus Locator", "BatterySeconds", 120f, new ConfigDescription("Approximate powered-on lifetime of a full battery in Continuous and Radar modes.", (AcceptableValueBase)(object)new AcceptableValueRange(5f, 3600f), Array.Empty())); RadarIntervalSeconds = config.Bind("Apparatus Locator", "RadarIntervalSeconds", 3f, new ConfigDescription("Seconds between frozen reading updates in Radar mode.", (AcceptableValueBase)(object)new AcceptableValueRange(0.25f, 60f), Array.Empty())); ScanSoundVolume = config.Bind("Apparatus Locator", "ScanSoundVolume", 0.65f, new ConfigDescription("Volume of the locator's scan ping.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); ShowDistance = config.Bind("Apparatus Locator", "ShowDistance", true, "Show the distance to the current moon's docked apparatus."); ShowArrow = config.Bind("Apparatus Locator", "ShowArrow", false, "Show a compass arrow that points toward the apparatus."); DistanceUnits = config.Bind("Apparatus Locator", "DistanceUnits", DistanceUnit.Meters, "Distance units: Meters, Feet, or GameUnits."); RequireDetectionRange = config.Bind("Apparatus Locator", "RequireDetectionRange", false, "Only detect the apparatus while it is inside DetectionRange."); DetectionRange = config.Bind("Apparatus Locator", "DetectionRange", 50f, new ConfigDescription("Maximum detection distance when RequireDetectionRange is enabled, measured in the selected DistanceUnits.", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 5000f), Array.Empty())); } } internal static class StaminaBoostController { private static bool _wasActive; internal static bool IsActive { get { StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance != (Object)null && instance.shipHasLanded && !instance.inShipPhase) { return MeltdownAPI.MeltdownStarted; } return false; } } internal static void Tick() { bool isActive = IsActive; if (isActive != _wasActive) { _wasActive = isActive; if (isActive) { SyncedConfigSnapshot current = HostConfigSynchronizer.Current; Plugin.ModLog.LogInfo((object)("Meltdown stamina boosts enabled: " + $"+{current.ExtraStaminaPercent:0.##}% effective stamina, " + $"+{current.ExtraStaminaRegenPercent:0.##}% regeneration.")); } else { Plugin.ModLog.LogInfo((object)"Meltdown stamina boosts disabled."); } } } internal static void ForceDeactivate(string reason) { if (_wasActive) { Plugin.ModLog.LogInfo((object)("Meltdown stamina boosts disabled (" + reason + ").")); } _wasActive = false; } internal static StaminaSnapshot Capture(PlayerControllerB player) { if (!IsLocalPlayer(player) || !IsActive) { return default(StaminaSnapshot); } return new StaminaSnapshot(tracked: true, player.sprintMeter); } internal static void ApplyFrameDelta(PlayerControllerB player, StaminaSnapshot snapshot, bool includeRegeneration) { if (!snapshot.Tracked || !IsLocalPlayer(player) || !IsActive) { return; } float sprintMeter = player.sprintMeter; float num = sprintMeter - snapshot.Before; if (num < 0f) { SyncedConfigSnapshot current = HostConfigSynchronizer.Current; float num2 = 1f + current.ExtraStaminaPercent / 100f; float num3 = 0f - num; player.sprintMeter = snapshot.Before - num3 / num2; } else if (includeRegeneration && num > 0f) { SyncedConfigSnapshot current2 = HostConfigSynchronizer.Current; float num4 = num * current2.ExtraStaminaRegenPercent / 100f; float num5 = sprintMeter + num4; if (snapshot.Before > 1f || sprintMeter > 1f) { player.sprintMeter = num5; } else { player.sprintMeter = Mathf.Min(1f, num5); } } if ((Object)(object)player.sprintMeterUI != (Object)null) { player.sprintMeterUI.fillAmount = player.sprintMeter; } } private static bool IsLocalPlayer(PlayerControllerB player) { if ((Object)(object)GameNetworkManager.Instance != (Object)null) { return player == GameNetworkManager.Instance.localPlayerController; } return false; } } internal readonly struct StaminaSnapshot { internal bool Tracked { get; } internal float Before { get; } internal StaminaSnapshot(bool tracked, float before) { Tracked = tracked; Before = before; } } [HarmonyPatch(typeof(PlayerControllerB), "Update")] internal static class MeltdownDistanceSpeedPatch { [HarmonyPrefix] [HarmonyPriority(0)] private static void Prefix(PlayerControllerB __instance, out MovementSpeedSnapshot __state) { __state = MovementSpeedSnapshot.Capture(__instance); if (__state.AddedSpeed > 0f) { __instance.movementSpeed += __state.AddedSpeed; } } [HarmonyPostfix] [HarmonyPriority(800)] private static void Postfix(PlayerControllerB __instance, MovementSpeedSnapshot __state) { if (__state.AddedSpeed > 0f) { __instance.movementSpeed -= __state.AddedSpeed; } } } internal readonly struct MovementSpeedSnapshot { internal float AddedSpeed { get; } private MovementSpeedSnapshot(float addedSpeed) { AddedSpeed = addedSpeed; } internal static MovementSpeedSnapshot Capture(PlayerControllerB player) { //IL_006f: 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) if (!StaminaBoostController.IsActive || !IsLocalPlayer(player) || player.isInsideFactory || player.isInHangarShipRoom) { return default(MovementSpeedSnapshot); } SyncedConfigSnapshot current = HostConfigSynchronizer.Current; if (!current.EnableDistanceSpeedBoost) { return default(MovementSpeedSnapshot); } Transform val = StartOfRound.Instance?.shipDoorNode; if ((Object)(object)val == (Object)null) { return default(MovementSpeedSnapshot); } float num = Vector3.Distance(((Component)player).transform.position, val.position); float num2; if (num >= current.SecondSpeedDistance) { num2 = current.SecondSpeedPercent; } else { if (!(num >= current.FirstSpeedDistance)) { return default(MovementSpeedSnapshot); } num2 = current.FirstSpeedPercent; } float num3 = player.movementSpeed * num2 / 100f; if (!(num3 > 0f)) { return default(MovementSpeedSnapshot); } return new MovementSpeedSnapshot(num3); } private static bool IsLocalPlayer(PlayerControllerB player) { if ((Object)(object)GameNetworkManager.Instance != (Object)null) { return player == GameNetworkManager.Instance.localPlayerController; } return false; } } [HarmonyPatch(typeof(PlayerControllerB), "LateUpdate")] internal static class PlayerStaminaLateUpdatePatch { [HarmonyPrefix] [HarmonyPriority(800)] private static void Prefix(PlayerControllerB __instance, out StaminaSnapshot __state) { __state = StaminaBoostController.Capture(__instance); } [HarmonyPostfix] [HarmonyPriority(0)] private static void Postfix(PlayerControllerB __instance, StaminaSnapshot __state) { StaminaBoostController.ApplyFrameDelta(__instance, __state, includeRegeneration: true); } } [HarmonyPatch(typeof(PlayerControllerB), "Jump_performed")] internal static class PlayerJumpStaminaPatch { [HarmonyPrefix] [HarmonyPriority(800)] private static void Prefix(PlayerControllerB __instance, out StaminaSnapshot __state) { __state = StaminaBoostController.Capture(__instance); } [HarmonyPostfix] [HarmonyPriority(0)] private static void Postfix(PlayerControllerB __instance, StaminaSnapshot __state) { StaminaBoostController.ApplyFrameDelta(__instance, __state, includeRegeneration: false); } } [HarmonyPatch(typeof(MeltdownHandler), "HandleEnemies")] internal static class FacilityMeltdownEnemyCompatibilityPatch { [HarmonyFinalizer] private static Exception? Finalizer(Exception? __exception) { if (__exception == null) { return null; } Plugin.ModLog.LogError((object)("FacilityMeltdown's optional enemy handling failed. The exception was contained so host-side meltdown initialization and forced takeoff can continue. " + $"Original error: {__exception}")); return null; } } [HarmonyPatch(typeof(MeltdownHandler), "Update")] internal static class FacilityMeltdownUpdatePatch { [HarmonyPostfix] private static void Postfix(MeltdownHandler __instance) { ForcedTakeoffFailsafe.Observe(__instance); MeltdownHudController.Observe(__instance); } } [HarmonyPatch(typeof(MeltdownHandler), "OnDisable")] internal static class FacilityMeltdownCleanupPatch { [HarmonyPrefix] private static void Prefix() { StaminaBoostController.ForceDeactivate("meltdown handler cleanup"); ForcedTakeoffFailsafe.Reset(); if (MeltdownDepartureController.IsShipDeparting) { MeltdownDepartureController.Begin(); return; } LazyMusicPlayer.Stop(); MeltdownHudController.ResetSession(); } } [HarmonyPatch(typeof(StartOfRound), "ShipLeave")] internal static class ShipLeaveCleanupPatch { [HarmonyPrefix] private static void Prefix() { StaminaBoostController.ForceDeactivate("ship departure"); MeltdownDepartureController.Begin(); } } [HarmonyPatch(typeof(StartOfRound), "EndOfGame")] internal static class EndOfGameCleanupPatch { [HarmonyPrefix] private static void Prefix() { StaminaBoostController.ForceDeactivate("end of round"); MeltdownDepartureController.Begin(); } } internal static class MeltdownDepartureController { internal static bool IsShipDeparting { get { StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance != (Object)null) { if (!instance.shipIsLeaving && instance.shipHasLanded) { return instance.inShipPhase; } return true; } return false; } } internal static void Begin() { float departureFadeSeconds = HostConfigSynchronizer.Current.DepartureFadeSeconds; LazyMusicPlayer.BeginFadeOut(departureFadeSeconds); MeltdownHudController.BeginFadeOut(departureFadeSeconds); } internal static void StopImmediatelyOrFade() { if (IsShipDeparting) { Begin(); } else { LazyMusicPlayer.Stop(); } } } [HarmonyPatch(typeof(StartOfRound), "SetSpectateCameraToGameOverMode")] internal static class MeltdownAlivePlayerSpectateFix { [HarmonyPrefix] private static bool Prefix(bool enableGameOver, PlayerControllerB? localPlayer) { if (!enableGameOver || (Object)(object)localPlayer != (Object)null || !MeltdownAPI.MeltdownStarted) { return true; } PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController; StartOfRound instance = StartOfRound.Instance; if ((Object)(object)val == (Object)null || (Object)(object)instance == (Object)null || val.isPlayerDead || !instance.shipIsLeaving || (!val.isInElevator && !val.isInHangarShipRoom)) { return true; } Plugin.ModLog.LogInfo((object)"Prevented FacilityMeltdown from forcing a living ship survivor into the game-over spectator camera."); return false; } } internal static class ForcedTakeoffFailsafe { private const float GraceSecondsAfterOriginalTakeoff = 7f; private const float RetryDelaySeconds = 2f; private const int MaximumAttempts = 3; private static readonly MethodInfo? ShipLeaveMethod = AccessTools.Method(typeof(StartOfRound), "ShipLeave", (Type[])null, (Type[])null); private static bool _armed; private static float _nextAttemptTime; private static int _attempts; internal static void Observe(MeltdownHandler handler) { if (!_armed && IsHost() && !(handler.TimeLeftUntilMeltdown > 3f)) { _armed = true; _attempts = 0; _nextAttemptTime = Time.realtimeSinceStartup + 7f; Plugin.ModLog.LogDebug((object)"Armed the host forced-takeoff failsafe."); } } internal static void Tick() { if (!_armed) { return; } StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null || instance.inShipPhase || !MeltdownAPI.MeltdownStarted) { Reset(); } else { if (!IsHost() || !instance.shipHasLanded || Time.realtimeSinceStartup < _nextAttemptTime || _attempts >= 3) { return; } _attempts++; _nextAttemptTime = Time.realtimeSinceStartup + 2f; if (!(ShipLeaveMethod == null)) { try { Plugin.ModLog.LogWarning((object)("FacilityMeltdown's host departure did not complete; running takeoff " + $"failsafe attempt {_attempts}/{3}.")); instance.shipLeftAutomatically = true; instance.shipIsLeaving = true; ShipLeaveMethod.Invoke(instance, Array.Empty()); return; } catch (Exception arg) { Plugin.ModLog.LogError((object)$"Forced-takeoff failsafe attempt {_attempts} failed: {arg}"); return; } } Plugin.ModLog.LogError((object)"Could not locate StartOfRound.ShipLeave for the forced-takeoff failsafe."); _attempts = 3; } } internal static void Reset() { _armed = false; _nextAttemptTime = 0f; _attempts = 0; } private static bool IsHost() { NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton != (Object)null && singleton.IsListening) { return singleton.IsHost; } return false; } } internal static class MeltdownHudController { private const float DistanceUpdateInterval = 0.1f; private const float NaturalTrackRightMargin = 24f; private static MeltdownHandler? _handler; private static HUDManager? _hudManager; private static GameObject? _root; private static RectTransform? _rootRect; private static CanvasGroup? _canvasGroup; private static Canvas? _hudCanvas; private static RectTransform? _timerRoot; private static Image? _timerPanel; private static Image? _timerHighlight; private static TextMeshProUGUI? _timerHeader; private static TextMeshProUGUI? _timerSeconds; private static TextMeshProUGUI? _timerHundredths; private static RectTransform? _distanceRoot; private static TextMeshProUGUI? _distanceText; private static RectTransform? _trackRoot; private static TextMeshProUGUI? _trackHeader; private static TextMeshProUGUI? _trackTitle; private static TextMeshProUGUI? _trackAuthor; private static RectTransform? _discRect; private static Image? _discImage; private static Image? _tonearmImage; private static Texture2D? _discTexture; private static Sprite? _discSprite; private static Texture2D? _tonearmTexture; private static Sprite? _tonearmSprite; private static Texture2D? _timerPanelTexture; private static Sprite? _timerPanelSprite; private static Texture2D? _timerHighlightTexture; private static Sprite? _timerHighlightSprite; private static TrackInfo? _currentTrack; private static double _trackStartDsp; private static double _sessionStartDsp; private static double _lastPulseDsp = double.NegativeInfinity; private static long _lastPulseIndex = long.MinValue; private static float _lastPulseInterval = -1f; private static float _lastPulseOffset = -1f; private static bool _wasActive; private static float _nextDistanceUpdate; private static bool _hasEntrancePosition; private static Vector3 _entrancePosition; private static float _discAngle; private static bool _timerPositionInitialized; private static bool _fadingOut; private static float _fadeStartTime; private static float _fadeDuration; private static float _fadeStartOpacity; private static string? _cachedBaseColorText; private static string? _cachedFlashColorText; private static Color _cachedBaseColor = new Color(1f, 0.125f, 0.125f, 1f); private static Color _cachedFlashColor = new Color(1f, 0.824f, 0.102f, 1f); internal static void Observe(MeltdownHandler handler) { _handler = handler; } internal static void OnTrackStarted(string filename) { _currentTrack = TrackMetadata.Resolve(filename); _trackStartDsp = AudioSettings.dspTime; _lastPulseDsp = double.NegativeInfinity; _lastPulseIndex = long.MinValue; _lastPulseInterval = -1f; _lastPulseOffset = -1f; UpdateTrackText(); Plugin.ModLog.LogInfo((object)("HUD track metadata: '" + _currentTrack.Title + "', " + (_currentTrack.HasBpm ? $"{_currentTrack.Bpm:0.###} BPM." : "fixed-interval pulse fallback."))); } internal static void Tick() { //IL_00d3: Unknown result type (might be due to invalid IL or missing references) if (_fadingOut) { UpdateDepartureFade(); return; } SyncedConfigSnapshot current = HostConfigSynchronizer.Current; if (!current.EnableHud || !IsMeltdownActive()) { if (_wasActive) { Hide(); } _wasActive = false; return; } if (!_wasActive) { _sessionStartDsp = AudioSettings.dspTime; _lastPulseIndex = long.MinValue; _lastPulseDsp = double.NegativeInfinity; } _wasActive = true; HUDManager instance = HUDManager.Instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance.HUDContainer == (Object)null) { return; } EnsureCreated(instance); if (!((Object)(object)_root == (Object)null)) { if (!instance.HUDContainer.activeInHierarchy) { Hide(); return; } _root.SetActive(true); UpdateVisibilityAndLayout(current); UpdateTimer(); UpdateDistance(current); UpdateDisc(current); ApplySharedColor(CalculatePulseColor(current), current); } } internal static void BeginFadeOut(float seconds) { if (!_fadingOut) { if (seconds <= 0f || (Object)(object)_root == (Object)null || !_root.activeSelf || (Object)(object)_canvasGroup == (Object)null) { ResetSession(); return; } _handler = null; _wasActive = false; _fadingOut = true; _fadeStartTime = Time.unscaledTime; _fadeDuration = Mathf.Max(0.01f, seconds); _fadeStartOpacity = _canvasGroup.alpha; Plugin.ModLog.LogDebug((object)$"Fading meltdown HUD out over {_fadeDuration:0.##} seconds."); } } internal static void ResetSession() { _handler = null; _currentTrack = null; _trackStartDsp = 0.0; _sessionStartDsp = 0.0; _lastPulseDsp = double.NegativeInfinity; _lastPulseIndex = long.MinValue; _lastPulseInterval = -1f; _lastPulseOffset = -1f; _nextDistanceUpdate = 0f; _hasEntrancePosition = false; _timerPositionInitialized = false; _wasActive = false; _fadingOut = false; _fadeStartTime = 0f; _fadeDuration = 0f; _fadeStartOpacity = 0f; Hide(); UpdateTrackText(); } private static void UpdateDepartureFade() { if ((Object)(object)_root == (Object)null || (Object)(object)_canvasGroup == (Object)null) { ResetSession(); return; } float num = Mathf.Clamp01((Time.unscaledTime - _fadeStartTime) / _fadeDuration); _canvasGroup.alpha = _fadeStartOpacity * (1f - num); if (num >= 1f) { ResetSession(); } } internal static void Destroy() { ResetSession(); if ((Object)(object)_root != (Object)null) { Object.Destroy((Object)(object)_root); } if ((Object)(object)_discSprite != (Object)null) { Object.Destroy((Object)(object)_discSprite); } if ((Object)(object)_discTexture != (Object)null) { Object.Destroy((Object)(object)_discTexture); } if ((Object)(object)_tonearmSprite != (Object)null) { Object.Destroy((Object)(object)_tonearmSprite); } if ((Object)(object)_tonearmTexture != (Object)null) { Object.Destroy((Object)(object)_tonearmTexture); } if ((Object)(object)_timerPanelSprite != (Object)null) { Object.Destroy((Object)(object)_timerPanelSprite); } if ((Object)(object)_timerPanelTexture != (Object)null) { Object.Destroy((Object)(object)_timerPanelTexture); } if ((Object)(object)_timerHighlightSprite != (Object)null) { Object.Destroy((Object)(object)_timerHighlightSprite); } if ((Object)(object)_timerHighlightTexture != (Object)null) { Object.Destroy((Object)(object)_timerHighlightTexture); } _root = null; _rootRect = null; _canvasGroup = null; _hudCanvas = null; _timerRoot = null; _timerPanel = null; _timerHighlight = null; _timerHeader = null; _timerSeconds = null; _timerHundredths = null; _distanceRoot = null; _distanceText = null; _trackRoot = null; _trackHeader = null; _trackTitle = null; _trackAuthor = null; _discRect = null; _discImage = null; _tonearmImage = null; _discTexture = null; _discSprite = null; _tonearmTexture = null; _tonearmSprite = null; _timerPanelTexture = null; _timerPanelSprite = null; _timerHighlightTexture = null; _timerHighlightSprite = null; _hudManager = null; VcrFontFactory.Destroy(); } private static bool IsMeltdownActive() { StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance != (Object)null && instance.shipHasLanded && !instance.inShipPhase) { return MeltdownAPI.MeltdownStarted; } return false; } private static void EnsureCreated(HUDManager hudManager) { //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Expected O, but got Unknown //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Expected O, but got Unknown if (!((Object)(object)_root != (Object)null) || _hudManager != hudManager) { if ((Object)(object)_root != (Object)null) { Object.Destroy((Object)(object)_root); } _hudManager = hudManager; TMP_FontAsset orCreate = VcrFontFactory.GetOrCreate(); _root = new GameObject("CombinedMeltdownPlaylist HUD", new Type[2] { typeof(RectTransform), typeof(CanvasGroup) }); Canvas componentInParent = hudManager.HUDContainer.GetComponentInParent(); Canvas val = (_hudCanvas = ((componentInParent != null) ? componentInParent.rootCanvas : null)); Transform val2 = (((Object)(object)val != (Object)null) ? ((Component)val).transform : hudManager.HUDContainer.transform); _root.transform.SetParent(val2, false); _root.transform.SetAsLastSibling(); _rootRect = (RectTransform)_root.transform; StretchToParent(_rootRect); _canvasGroup = _root.GetComponent(); _canvasGroup.interactable = false; _canvasGroup.blocksRaycasts = false; BuildTimer(orCreate); BuildDistance(orCreate); BuildTrackPanel(orCreate); UpdateTrackText(); Plugin.ModLog.LogInfo((object)"Created the meltdown HUD."); } } private static void BuildTimer(TMP_FontAsset font) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0032: 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) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: 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_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Unknown result type (might be due to invalid IL or missing references) //IL_01e5: Unknown result type (might be due to invalid IL or missing references) _timerRoot = CreateRect(_rootRect, "Reactor Timer", new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(184f, 86f), Vector2.zero); _timerPanel = ((Component)CreateRect(_timerRoot, "Rounded Reactor Enclosure", Vector2.zero, Vector2.zero, Vector2.zero, new Vector2(184f, 86f), Vector2.zero)).gameObject.AddComponent(); ((Graphic)_timerPanel).raycastTarget = false; _timerPanelSprite = CreateTimerPanelSprite(); _timerPanel.sprite = _timerPanelSprite; _timerHighlight = ((Component)CreateRect(_timerRoot, "Reactor Border And Separator", Vector2.zero, Vector2.zero, Vector2.zero, new Vector2(184f, 86f), Vector2.zero)).gameObject.AddComponent(); ((Graphic)_timerHighlight).raycastTarget = false; _timerHighlightSprite = CreateTimerHighlightSprite(); _timerHighlight.sprite = _timerHighlightSprite; _timerHeader = CreateText(_timerRoot, "Critical Failure Label", font, 15f, (TextAlignmentOptions)514, new Vector2(8f, 61f), new Vector2(168f, 20f)); ((TMP_Text)_timerHeader).text = "CRITICAL FAILURE"; _timerSeconds = CreateText(_timerRoot, "Reactor Seconds", font, 51f, (TextAlignmentOptions)4100, new Vector2(7f, 4f), new Vector2(120f, 52f)); ((TMP_Text)_timerSeconds).text = "150"; _timerHundredths = CreateText(_timerRoot, "Reactor Hundredths", font, 21f, (TextAlignmentOptions)1025, new Vector2(131f, 9f), new Vector2(45f, 31f)); ((TMP_Text)_timerHundredths).text = "00"; } private static void BuildDistance(TMP_FontAsset font) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0032: 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) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) _distanceRoot = CreateRect(_rootRect, "Distance", new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0f, 0f), new Vector2(380f, 44f), Vector2.zero); _distanceText = CreateText(_distanceRoot, "Distance Text", font, 25f, (TextAlignmentOptions)1025, Vector2.zero, new Vector2(380f, 44f)); } private static void BuildTrackPanel(TMP_FontAsset font) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0032: 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) //IL_0046: 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_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: 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_011a: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_01fa: Unknown result type (might be due to invalid IL or missing references) //IL_0209: Unknown result type (might be due to invalid IL or missing references) //IL_0237: Unknown result type (might be due to invalid IL or missing references) //IL_0246: Unknown result type (might be due to invalid IL or missing references) _trackRoot = CreateRect(_rootRect, "Now Playing", new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(1f, 0.5f), new Vector2(338f, 82f), Vector2.zero); _discRect = CreateRect(_trackRoot, "Spinning Disc", new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(52f, 52f), new Vector2(27f, 0f)); _discImage = ((Component)_discRect).gameObject.AddComponent(); ((Graphic)_discImage).raycastTarget = false; _discImage.preserveAspect = true; _discSprite = CreateDiscSprite(); _discImage.sprite = _discSprite; _tonearmImage = ((Component)CreateRect(_trackRoot, "Record Player Tonearm", new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(52f, 52f), new Vector2(30f, 4f))).gameObject.AddComponent(); ((Graphic)_tonearmImage).raycastTarget = false; _tonearmImage.preserveAspect = true; _tonearmSprite = CreateTonearmSprite(); _tonearmImage.sprite = _tonearmSprite; _trackHeader = CreateText(_trackRoot, "Now Playing Label", font, 13f, (TextAlignmentOptions)1025, new Vector2(59f, 54f), new Vector2(273f, 20f)); ((TMP_Text)_trackHeader).text = "NOW PLAYING"; _trackTitle = CreateText(_trackRoot, "Track Title", font, 21f, (TextAlignmentOptions)4097, new Vector2(59f, 27f), new Vector2(273f, 31f)); _trackAuthor = CreateText(_trackRoot, "Track Author", font, 13f, (TextAlignmentOptions)257, new Vector2(59f, 4f), new Vector2(273f, 23f)); } private static void UpdateVisibilityAndLayout(SyncedConfigSnapshot config) { //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_023f: Unknown result type (might be due to invalid IL or missing references) //IL_0244: Unknown result type (might be due to invalid IL or missing references) //IL_0253: Unknown result type (might be due to invalid IL or missing references) //IL_0258: 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_0281: 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_02a0: Unknown result type (might be due to invalid IL or missing references) //IL_02a5: Unknown result type (might be due to invalid IL or missing references) //IL_02b5: Unknown result type (might be due to invalid IL or missing references) //IL_02b7: Unknown result type (might be due to invalid IL or missing references) //IL_02d4: Unknown result type (might be due to invalid IL or missing references) //IL_02e3: Unknown result type (might be due to invalid IL or missing references) //IL_02ed: Unknown result type (might be due to invalid IL or missing references) //IL_02be: Unknown result type (might be due to invalid IL or missing references) //IL_02c3: Unknown result type (might be due to invalid IL or missing references) //IL_0334: Unknown result type (might be due to invalid IL or missing references) //IL_0339: Unknown result type (might be due to invalid IL or missing references) //IL_033d: Unknown result type (might be due to invalid IL or missing references) //IL_0351: Unknown result type (might be due to invalid IL or missing references) //IL_0356: Unknown result type (might be due to invalid IL or missing references) //IL_0367: Unknown result type (might be due to invalid IL or missing references) //IL_036c: Unknown result type (might be due to invalid IL or missing references) //IL_0371: Unknown result type (might be due to invalid IL or missing references) //IL_0382: Unknown result type (might be due to invalid IL or missing references) //IL_0387: Unknown result type (might be due to invalid IL or missing references) //IL_038c: Unknown result type (might be due to invalid IL or missing references) //IL_03a2: Unknown result type (might be due to invalid IL or missing references) //IL_03b1: Unknown result type (might be due to invalid IL or missing references) //IL_03c1: Unknown result type (might be due to invalid IL or missing references) //IL_0303: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_rootRect == (Object)null) && !((Object)(object)_timerRoot == (Object)null) && !((Object)(object)_distanceRoot == (Object)null) && !((Object)(object)_trackRoot == (Object)null) && !((Object)(object)_timerHundredths == (Object)null) && !((Object)(object)_canvasGroup == (Object)null)) { PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController; bool flag = (Object)(object)val == (Object)null || val.isPlayerDead; bool flag2 = (Object)(object)val != (Object)null && val.isInsideFactory; bool flag3 = (Object)(object)val != (Object)null && val.isInHangarShipRoom; float hudScale = config.HudScale; Vector3 localScale = Vector3.one * hudScale; ((Transform)_timerRoot).localScale = localScale; ((Transform)_distanceRoot).localScale = localScale; ((Transform)_trackRoot).localScale = localScale; _canvasGroup.alpha = config.HudOpacity; ((Component)_timerRoot).gameObject.SetActive(config.ShowReactorTimer); ((Component)_timerHundredths).gameObject.SetActive(config.ShowHundredths); if ((Object)(object)_timerPanel != (Object)null) { ((Component)_timerPanel).gameObject.SetActive(config.ShowTimerPanel); } if ((Object)(object)_timerHighlight != (Object)null) { ((Component)_timerHighlight).gameObject.SetActive(config.ShowTimerPanel); } bool active = config.ShowDistance && (Object)(object)val != (Object)null && !flag && !flag3; ((Component)_distanceRoot).gameObject.SetActive(active); bool flag4 = config.ShowSelectedTrack && _currentTrack != null; ((Component)_trackRoot).gameObject.SetActive(flag4); if ((Object)(object)_trackAuthor != (Object)null) { ((Component)_trackAuthor).gameObject.SetActive(flag4 && config.ShowTrackAuthor && !string.IsNullOrWhiteSpace(_currentTrack?.Author)); } if ((Object)(object)_discRect != (Object)null) { ((Component)_discRect).gameObject.SetActive(flag4 && config.ShowDisc); } if ((Object)(object)_tonearmImage != (Object)null) { ((Component)_tonearmImage).gameObject.SetActive(flag4 && config.ShowDisc); } Vector2 topCenterTimerTarget = GetTopCenterTimerTarget(config, hudScale); Vector2 val2 = (flag ? (ScreenToRootLocal(new Vector2(35f, 110f)) + new Vector2(_timerRoot.sizeDelta.x * hudScale * 0.5f + config.SpectatorTimerOffsetX, _timerRoot.sizeDelta.y * hudScale * 0.5f + config.SpectatorTimerOffsetY)) : ((!(flag2 || flag3) && IsOutsideClockVisible()) ? GetOutsideTimerTarget(config, hudScale) : topCenterTimerTarget)); SetAnchoredLayout(_timerRoot, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), _timerRoot.anchoredPosition); if (!_timerPositionInitialized) { _timerRoot.anchoredPosition = topCenterTimerTarget; _timerPositionInitialized = true; } float num = 1f - Mathf.Exp((0f - config.TimerTransitionSpeed) * Time.unscaledDeltaTime); _timerRoot.anchoredPosition = Vector2.Lerp(_timerRoot.anchoredPosition, val2, num); Vector2 anchoredPosition = ScreenToRootLocal(new Vector2(35f, 22f)) + new Vector2(config.DistanceOffsetX, config.DistanceOffsetY); SetAnchoredLayout(_distanceRoot, new Vector2(0.5f, 0.5f), Vector2.zero, anchoredPosition); SetAnchoredLayout(_trackRoot, new Vector2(1f, 0.5f), new Vector2(1f, 0.5f), new Vector2(0f, config.TrackOffsetY)); AlignTrackVisibleContentToRight(config); } } private static void AlignTrackVisibleContentToRight(SyncedConfigSnapshot config) { //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0168: 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_017f: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_018f: 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_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_trackRoot == (Object)null || !((Component)_trackRoot).gameObject.activeSelf || (Object)(object)_discRect == (Object)null || (Object)(object)_trackHeader == (Object)null || (Object)(object)_trackTitle == (Object)null || (Object)(object)_trackAuthor == (Object)null) { return; } RectTransform[] obj = new RectTransform[5] { _discRect, default(RectTransform), default(RectTransform), default(RectTransform), default(RectTransform) }; Image? tonearmImage = _tonearmImage; obj[1] = ((tonearmImage != null) ? ((Graphic)tonearmImage).rectTransform : null); obj[2] = ((TMP_Text)_trackHeader).rectTransform; obj[3] = ((TMP_Text)_trackTitle).rectTransform; obj[4] = ((TMP_Text)_trackAuthor).rectTransform; Camera hudCamera = GetHudCamera(); Vector3[] array = (Vector3[])(object)new Vector3[4]; float num = float.NegativeInfinity; RectTransform[] array2 = (RectTransform[])(object)obj; foreach (RectTransform val in array2) { if (!((Object)(object)val == (Object)null) && ((Component)val).gameObject.activeInHierarchy) { val.GetWorldCorners(array); Vector3[] array3 = array; foreach (Vector3 val2 in array3) { Vector2 val3 = RectTransformUtility.WorldToScreenPoint(hudCamera, val2); num = Mathf.Max(num, val3.x); } } } if (!float.IsNegativeInfinity(num)) { float num2 = ScreenToRootLocal(new Vector2((float)Screen.width - 24f + config.TrackOffsetX, (float)Screen.height * 0.5f)).x - ScreenToRootLocal(new Vector2(num, (float)Screen.height * 0.5f)).x; RectTransform? trackRoot = _trackRoot; trackRoot.anchoredPosition += new Vector2(num2, 0f); } } private static Vector2 GetTopCenterTimerTarget(SyncedConfigSnapshot config, float scale) { //IL_002b: 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) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_timerRoot == (Object)null) { return Vector2.zero; } return ScreenToRootLocal(new Vector2((float)Screen.width * 0.5f, (float)Screen.height - 24f)) + new Vector2(config.InsideTimerOffsetX, (0f - _timerRoot.sizeDelta.y) * scale * 0.5f + config.InsideTimerOffsetY); } private static Vector2 GetOutsideTimerTarget(SyncedConfigSnapshot config, float scale) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: 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_007b: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_timerRoot == (Object)null) { return GetTopCenterTimerTarget(config, scale); } HUDManager? hudManager = _hudManager; object obj; if (hudManager == null) { obj = null; } else { TextMeshProUGUI clockNumber = hudManager.clockNumber; obj = ((clockNumber != null) ? ((TMP_Text)clockNumber).rectTransform : null); } RectTransform val = (RectTransform)obj; if ((Object)(object)val == (Object)null) { return GetTopCenterTimerTarget(config, scale); } Rect rect = val.rect; float xMax = ((Rect)(ref rect)).xMax; rect = val.rect; Vector3 val2 = ((Transform)val).TransformPoint(new Vector3(xMax, ((Rect)(ref rect)).center.y, 0f)); return ScreenToRootLocal(RectTransformUtility.WorldToScreenPoint(GetHudCamera(), val2)) + new Vector2(50f + _timerRoot.sizeDelta.x * scale * 0.5f + config.OutsideTimerOffsetX, config.OutsideTimerOffsetY); } private static bool IsOutsideClockVisible() { TextMeshProUGUI val = _hudManager?.clockNumber; if ((Object)(object)val != (Object)null && ((Behaviour)val).enabled) { return ((Component)val).gameObject.activeInHierarchy; } return false; } private static Vector2 ScreenToRootLocal(Vector2 screenPosition) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) Vector2 result = default(Vector2); if ((Object)(object)_rootRect != (Object)null && RectTransformUtility.ScreenPointToLocalPointInRectangle(_rootRect, screenPosition, GetHudCamera(), ref result)) { return result; } return Vector2.zero; } private static Camera? GetHudCamera() { //IL_0012: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_hudCanvas == (Object)null || (int)_hudCanvas.renderMode == 0) { return null; } return _hudCanvas.worldCamera; } private static void UpdateTimer() { if (!((Object)(object)_timerSeconds == (Object)null) && !((Object)(object)_timerHundredths == (Object)null)) { int num = Mathf.FloorToInt((((Object)(object)_handler == (Object)null) ? 0f : Mathf.Max(0f, _handler.TimeLeftUntilMeltdown)) * 100f + 0.001f); int num2 = num / 100; int num3 = num % 100; ((TMP_Text)_timerSeconds).text = num2.ToString(CultureInfo.InvariantCulture); ((TMP_Text)_timerHundredths).text = num3.ToString("00", CultureInfo.InvariantCulture); } } private static void UpdateDistance(SyncedConfigSnapshot config) { //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_distanceRoot == (Object)null || !((Component)_distanceRoot).gameObject.activeSelf || (Object)(object)_distanceText == (Object)null || Time.unscaledTime < _nextDistanceUpdate) { return; } _nextDistanceUpdate = Time.unscaledTime + 0.1f; PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController; if ((Object)(object)val == (Object)null) { return; } Vector3 val2; string text; if (val.isInsideFactory) { if (!_hasEntrancePosition) { _entrancePosition = RoundManager.FindMainEntrancePosition(true, false); _hasEntrancePosition = _entrancePosition != Vector3.zero; } if (!_hasEntrancePosition) { ((TMP_Text)_distanceText).text = "ENTRANCE --"; return; } val2 = _entrancePosition; text = "ENTRANCE"; } else { Transform val3 = StartOfRound.Instance?.shipDoorNode; if ((Object)(object)val3 == (Object)null) { ((TMP_Text)_distanceText).text = "SHIP --"; return; } val2 = val3.position; text = "SHIP"; } float num = Vector3.Distance(((Component)val).transform.position, val2); string text2; switch (config.DistanceUnits) { case DistanceUnit.Feet: num *= 3.28084f; text2 = "FT"; break; case DistanceUnit.GameUnits: text2 = "U"; break; default: text2 = "M"; break; } int distanceDecimals = config.DistanceDecimals; string text3 = "F" + distanceDecimals.ToString(CultureInfo.InvariantCulture); ((TMP_Text)_distanceText).text = text + " " + num.ToString(text3, CultureInfo.InvariantCulture) + " " + text2; } private static void UpdateDisc(SyncedConfigSnapshot config) { //IL_0054: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_discRect == (Object)null) && ((Component)_discRect).gameObject.activeSelf) { _discAngle = Mathf.Repeat(_discAngle - config.DiscSpinSpeed * Time.unscaledDeltaTime, 360f); ((Transform)_discRect).localRotation = Quaternion.Euler(0f, 0f, _discAngle); } } private static Color CalculatePulseColor(SyncedConfigSnapshot config) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_01c3: Unknown result type (might be due to invalid IL or missing references) //IL_01c5: Unknown result type (might be due to invalid IL or missing references) //IL_01c6: Unknown result type (might be due to invalid IL or missing references) //IL_01d1: Unknown result type (might be due to invalid IL or missing references) Color val = ResolveConfiguredColor(config.BaseColor, ref _cachedBaseColorText, ref _cachedBaseColor, new Color(1f, 0.125f, 0.125f, 1f), "BaseColor"); Color val2 = ResolveConfiguredColor(config.FlashColor, ref _cachedFlashColorText, ref _cachedFlashColor, new Color(1f, 0.824f, 0.102f, 1f), "FlashColor"); if (!config.EnableFlashing) { return val; } float num2; float num3; double num4; if (config.UseTrackBpm && _currentTrack != null && _currentTrack.HasBpm) { int num = ((config.BeatCadence != HudBeatCadence.EveryFourBeats) ? 1 : 4); num2 = 60f / _currentTrack.Bpm * (float)num; num3 = Mathf.Max(0f, _currentTrack.BeatOffsetSeconds); num4 = _trackStartDsp; } else { num2 = Mathf.Clamp(config.FixedFlashIntervalSeconds, 0.25f, 30f); num3 = 0f; num4 = ((_trackStartDsp > 0.0) ? _trackStartDsp : _sessionStartDsp); } if (!Mathf.Approximately(num2, _lastPulseInterval) || !Mathf.Approximately(num3, _lastPulseOffset)) { _lastPulseInterval = num2; _lastPulseOffset = num3; _lastPulseIndex = long.MinValue; _lastPulseDsp = double.NegativeInfinity; } double dspTime = AudioSettings.dspTime; double num5 = dspTime - num4 - (double)num3; if (num5 >= 0.0) { long num6 = (long)Math.Floor(num5 / (double)num2); if (num6 != _lastPulseIndex) { _lastPulseIndex = num6; _lastPulseDsp = num4 + (double)num3 + (double)((float)num6 * num2); } } float num7 = Mathf.Min(config.FlashFadeSeconds, num2 * 0.75f); float num8 = (float)(dspTime - _lastPulseDsp); if (num8 < 0f || num8 >= num7) { return val; } return Color.Lerp(val2, val, Mathf.Clamp01(num8 / num7)); } private static Color ResolveConfiguredColor(string configuredValue, ref string? cachedText, ref Color cachedColor, Color fallback, string settingName) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_003b: 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 (string.Equals(cachedText, configuredValue, StringComparison.Ordinal)) { return cachedColor; } cachedText = configuredValue; Color val = default(Color); if (ColorUtility.TryParseHtmlString(configuredValue.StartsWith("#", StringComparison.Ordinal) ? configuredValue : ("#" + configuredValue), ref val)) { cachedColor = val; return val; } cachedColor = fallback; Plugin.ModLog.LogWarning((object)("Invalid HUD " + settingName + " '" + configuredValue + "'; using the default.")); return fallback; } private static void ApplySharedColor(Color color, SyncedConfigSnapshot config) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0031: 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_0047: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) ApplyTextColor(_timerHeader, color); ApplyTextColor(_timerSeconds, color); ApplyTextColor(_timerHundredths, color); ApplyTextColor(_distanceText, color); ApplyTextColor(_trackHeader, color); ApplyTextColor(_trackTitle, color); ApplyTextColor(_trackAuthor, color); if ((Object)(object)_discImage != (Object)null) { ((Graphic)_discImage).color = new Color(color.r, color.g, color.b, color.a * 0.78f); } if ((Object)(object)_tonearmImage != (Object)null) { ((Graphic)_tonearmImage).color = new Color(color.r, color.g, color.b, color.a * 0.88f); } if ((Object)(object)_timerPanel != (Object)null) { ((Graphic)_timerPanel).color = new Color(0f, 0f, 0f, config.TimerPanelOpacity); } if ((Object)(object)_timerHighlight != (Object)null) { ((Graphic)_timerHighlight).color = new Color(color.r, color.g, color.b, color.a * 0.9f); } } private static void ApplyTextColor(TextMeshProUGUI? text, Color color) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)text != (Object)null) { ((Graphic)text).color = color; } } private static void UpdateTrackText() { if (!((Object)(object)_trackTitle == (Object)null) && !((Object)(object)_trackAuthor == (Object)null)) { if (_currentTrack == null) { ((TMP_Text)_trackTitle).text = string.Empty; ((TMP_Text)_trackAuthor).text = string.Empty; } else { ((TMP_Text)_trackTitle).text = _currentTrack.Title; ((TMP_Text)_trackAuthor).text = (string.IsNullOrWhiteSpace(_currentTrack.Author) ? string.Empty : ("BY " + _currentTrack.Author)); } } } private static TextMeshProUGUI CreateText(RectTransform parent, string name, TMP_FontAsset font, float fontSize, TextAlignmentOptions alignment, Vector2 anchoredPosition, Vector2 size) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) TextMeshProUGUI obj = ((Component)CreateRect(parent, name, Vector2.zero, Vector2.zero, Vector2.zero, size, anchoredPosition)).gameObject.AddComponent(); ((TMP_Text)obj).font = font; ((TMP_Text)obj).fontSize = fontSize; ((TMP_Text)obj).fontStyle = (FontStyles)0; ((TMP_Text)obj).alignment = alignment; ((TMP_Text)obj).enableWordWrapping = false; ((TMP_Text)obj).overflowMode = (TextOverflowModes)1; ((Graphic)obj).raycastTarget = false; ((TMP_Text)obj).outlineWidth = 0.12f; ((TMP_Text)obj).outlineColor = new Color32((byte)20, (byte)0, (byte)0, (byte)210); ((TMP_Text)obj).margin = Vector4.zero; return obj; } private static RectTransform CreateRect(RectTransform parent, string name, Vector2 anchorMin, Vector2 anchorMax, Vector2 pivot, Vector2 size, Vector2 anchoredPosition) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0033: 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_003a: 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) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Expected O, but got Unknown RectTransform val = (RectTransform)new GameObject(name, new Type[1] { typeof(RectTransform) }).transform; ((Transform)val).SetParent((Transform)(object)parent, false); val.anchorMin = anchorMin; val.anchorMax = anchorMax; val.pivot = pivot; val.sizeDelta = size; val.anchoredPosition = anchoredPosition; return val; } private static void StretchToParent(RectTransform rect) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) rect.anchorMin = Vector2.zero; rect.anchorMax = Vector2.one; rect.pivot = new Vector2(0.5f, 0.5f); rect.offsetMin = Vector2.zero; rect.offsetMax = Vector2.zero; } private static void SetAnchoredLayout(RectTransform rect, Vector2 anchor, Vector2 pivot, Vector2 anchoredPosition) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000f: 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) rect.anchorMin = anchor; rect.anchorMax = anchor; rect.pivot = pivot; rect.anchoredPosition = anchoredPosition; } private static Sprite CreateDiscSprite() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: 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) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0074: 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_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) _discTexture = new Texture2D(64, 64, (TextureFormat)4, false) { name = "Combined Meltdown Music Disc", filterMode = (FilterMode)1, wrapMode = (TextureWrapMode)1, hideFlags = (HideFlags)61 }; Color[] array = (Color[])(object)new Color[4096]; Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(31.5f, 31.5f); float num = 30.08f; float num2 = 7.68f; for (int i = 0; i < 64; i++) { for (int j = 0; j < 64; j++) { Vector2 val2 = new Vector2((float)j, (float)i) - val; float magnitude = ((Vector2)(ref val2)).magnitude; Color clear = Color.clear; if (magnitude <= num && magnitude >= num2) { float num3 = Mathf.SmoothStep(0f, 1f, (num - magnitude) / 1.5f); float num4 = 0.48f + 0.18f * Mathf.Sin(magnitude * 1.35f); float num5 = Mathf.Clamp01((val2.x - val2.y + 8f) / 18f) * 0.18f; ((Color)(ref clear))..ctor(1f, 1f, 1f, Mathf.Clamp01((num4 + num5) * num3)); } array[i * 64 + j] = clear; } } _discTexture.SetPixels(array); _discTexture.Apply(false, true); Sprite obj = Sprite.Create(_discTexture, new Rect(0f, 0f, 64f, 64f), new Vector2(0.5f, 0.5f), 64f); ((Object)obj).name = "Combined Meltdown Music Disc"; ((Object)obj).hideFlags = (HideFlags)61; return obj; } private static Sprite CreateTonearmSprite() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: 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) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) _tonearmTexture = new Texture2D(64, 64, (TextureFormat)4, false) { name = "Combined Meltdown Record Player Tonearm", filterMode = (FilterMode)1, wrapMode = (TextureWrapMode)1, hideFlags = (HideFlags)61 }; Color[] array = (Color[])(object)new Color[4096]; Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(54f, 55f); Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(45f, 43f); Vector2 end = default(Vector2); ((Vector2)(ref end))..ctor(39f, 37f); for (int i = 0; i < 64; i++) { for (int j = 0; j < 64; j++) { Vector2 val3 = new Vector2((float)j, (float)i); float num = Vector2.Distance(val3, val); float num2 = DistanceToSegment(val3, val, val2); float num3 = DistanceToSegment(val3, val2, end); float num4 = 0f; if (num <= 5.5f) { num4 = ((num >= 2.4f) ? 0.95f : 0.32f); } if (num2 <= 1.8f) { num4 = Mathf.Max(num4, 0.92f); } if (num3 <= 3.1f) { num4 = Mathf.Max(num4, 1f); } array[i * 64 + j] = new Color(1f, 1f, 1f, num4); } } _tonearmTexture.SetPixels(array); _tonearmTexture.Apply(false, true); Sprite obj = Sprite.Create(_tonearmTexture, new Rect(0f, 0f, 64f, 64f), new Vector2(0.5f, 0.5f), 64f); ((Object)obj).name = "Combined Meltdown Record Player Tonearm"; ((Object)obj).hideFlags = (HideFlags)61; return obj; } private static Sprite CreateTimerPanelSprite() { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) _timerPanelTexture = new Texture2D(184, 86, (TextureFormat)4, false) { name = "Combined Meltdown Rounded Reactor Panel", filterMode = (FilterMode)1, wrapMode = (TextureWrapMode)1, hideFlags = (HideFlags)61 }; Color[] array = (Color[])(object)new Color[15824]; Vector2 center = default(Vector2); ((Vector2)(ref center))..ctor(91.5f, 42.5f); Vector2 halfSize = default(Vector2); ((Vector2)(ref halfSize))..ctor(91.5f, 42.5f); for (int i = 0; i < 86; i++) { for (int j = 0; j < 184; j++) { float num = ((RoundedRectangleDistance(j, i, center, halfSize, 10f) <= -1f) ? 0.95f : 0f); array[i * 184 + j] = new Color(1f, 1f, 1f, num); } } _timerPanelTexture.SetPixels(array); _timerPanelTexture.Apply(false, true); Sprite obj = Sprite.Create(_timerPanelTexture, new Rect(0f, 0f, 184f, 86f), new Vector2(0.5f, 0.5f), 100f); ((Object)obj).name = "Combined Meltdown Rounded Reactor Panel"; ((Object)obj).hideFlags = (HideFlags)61; return obj; } private static Sprite CreateTimerHighlightSprite() { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_0159: 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_0076: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) _timerHighlightTexture = new Texture2D(184, 86, (TextureFormat)4, false) { name = "Combined Meltdown Reactor Highlight", filterMode = (FilterMode)1, wrapMode = (TextureWrapMode)1, hideFlags = (HideFlags)61 }; Color[] array = (Color[])(object)new Color[15824]; Vector2 center = default(Vector2); ((Vector2)(ref center))..ctor(91.5f, 42.5f); Vector2 halfSize = default(Vector2); ((Vector2)(ref halfSize))..ctor(91.5f, 42.5f); for (int i = 0; i < 86; i++) { for (int j = 0; j < 184; j++) { float num = RoundedRectangleDistance(j, i, center, halfSize, 10f); bool num2 = num <= 0f && num >= -2f; bool flag = Mathf.Abs((float)i - 57f) <= 1f && j >= 9 && j < 175; float num3 = ((num2 || flag) ? 0.96f : 0f); array[i * 184 + j] = new Color(1f, 1f, 1f, num3); } } _timerHighlightTexture.SetPixels(array); _timerHighlightTexture.Apply(false, true); Sprite obj = Sprite.Create(_timerHighlightTexture, new Rect(0f, 0f, 184f, 86f), new Vector2(0.5f, 0.5f), 100f); ((Object)obj).name = "Combined Meltdown Reactor Highlight"; ((Object)obj).hideFlags = (HideFlags)61; return obj; } private static float RoundedRectangleDistance(float x, float y, Vector2 center, Vector2 halfSize, float cornerRadius) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: 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_0049: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) Vector2 val = new Vector2(Mathf.Abs(x - center.x), Mathf.Abs(y - center.y)) - (halfSize - Vector2.one * cornerRadius); Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(Mathf.Max(val.x, 0f), Mathf.Max(val.y, 0f)); return Mathf.Min(Mathf.Max(val.x, val.y), 0f) + ((Vector2)(ref val2)).magnitude - cornerRadius; } private static float DistanceToSegment(Vector2 point, Vector2 start, Vector2 end) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: 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_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) Vector2 val = end - start; float sqrMagnitude = ((Vector2)(ref val)).sqrMagnitude; if (sqrMagnitude <= Mathf.Epsilon) { return Vector2.Distance(point, start); } float num = Mathf.Clamp01(Vector2.Dot(point - start, val) / sqrMagnitude); return Vector2.Distance(point, start + val * num); } private static void Hide() { if ((Object)(object)_root != (Object)null) { _root.SetActive(false); } } } [BepInPlugin("combinedmeltdownplaylist.sync", "Combined Meltdown Playlist Sync", "1.6.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInProcess("Lethal Company.exe")] public sealed class Plugin : BaseUnityPlugin { [CompilerGenerated] private static class <>O { public static HandleNamedMessageDelegate <0>__HandleSelectionMessage; public static HandleNamedMessageDelegate <1>__Receive; } public const string PluginGuid = "combinedmeltdownplaylist.sync"; public const string PluginName = "Combined Meltdown Playlist Sync"; public const string PluginVersion = "1.6.0"; internal const string PlaylistTag = "CombinedMeltdownPlaylist"; internal const string SelectionMessageName = "CombinedMeltdownPlaylist.Selection.v2"; internal const string ConfigMessageName = "CombinedMeltdownPlaylist.Config.v2"; private Harmony? _harmony; internal static Plugin Instance { get; private set; } internal static ManualLogSource ModLog { get; private set; } internal static NetworkManager? RegisteredNetworkManager { get; private set; } internal static ConfigEntry ExtraStaminaPercent { get; private set; } internal static ConfigEntry ExtraStaminaRegenPercent { get; private set; } internal static ConfigEntry PlaylistMode { get; private set; } internal static ConfigEntry MusicVolume { get; private set; } internal static ConfigEntry DepartureFadeSeconds { get; private set; } private void Awake() { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Expected O, but got Unknown //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Expected O, but got Unknown //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Expected O, but got Unknown //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Expected O, but got Unknown //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Expected O, but got Unknown Instance = this; ModLog = ((BaseUnityPlugin)this).Logger; AcceptableValueRange val = new AcceptableValueRange(0f, 1000f); ExtraStaminaPercent = ((BaseUnityPlugin)this).Config.Bind("Meltdown Stamina", "ExtraStaminaPercent", 20f, new ConfigDescription("Percentage of extra effective stamina capacity during a meltdown. For example, 20 means 20% more stamina. Applied to the final stamina drain so upgrades from other mods remain included.", (AcceptableValueBase)(object)val, Array.Empty())); ExtraStaminaRegenPercent = ((BaseUnityPlugin)this).Config.Bind("Meltdown Stamina", "ExtraStaminaRegenPercent", 50f, new ConfigDescription("Percentage of extra stamina regeneration during a meltdown. For example, 50 means regeneration is 50% faster. Applied to the final regeneration produced by the game and other mods.", (AcceptableValueBase)(object)val, Array.Empty())); PlaylistMode = ((BaseUnityPlugin)this).Config.Bind("Music Selection", "PlaylistMode", false, "If false, every meltdown makes an independent equal random pick, so immediate repeats are possible. If true, the host uses a shuffled playlist that plays every track once before reshuffling."); MusicVolume = ((BaseUnityPlugin)this).Config.Bind("Music", "MusicVolume", 0.5f, new ConfigDescription("Volume applied to every combined meltdown track.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); DepartureFadeSeconds = ((BaseUnityPlugin)this).Config.Bind("Meltdown Departure", "FadeOutSeconds", 2f, new ConfigDescription("Seconds for the meltdown music and HUD to fade out together when the ship leaves. Set to 0 for immediate cleanup.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 10f), Array.Empty())); HudConfig.Bind(((BaseUnityPlugin)this).Config); LocatorConfig.Bind(((BaseUnityPlugin)this).Config); EscapeSpeedConfig.Bind(((BaseUnityPlugin)this).Config); HostConfigSynchronizer.Reset(); ApparatusLocatorRegistry.EnsureRegistered(); _harmony = new Harmony("combinedmeltdownplaylist.sync"); _harmony.PatchAll(); ModLog.LogInfo((object)"Combined Meltdown Playlist Sync 1.6.0 loaded."); } private void Update() { EnsureNetworkRegistration(); HostConfigSynchronizer.Tick(); SelectionCoordinator.Tick(); LazyMusicPlayer.Tick(); StaminaBoostController.Tick(); ForcedTakeoffFailsafe.Tick(); MeltdownHudController.Tick(); ApparatusLocatorRegistry.Tick(); } private void OnDestroy() { StaminaBoostController.ForceDeactivate("plugin shutdown"); ForcedTakeoffFailsafe.Reset(); LazyMusicPlayer.Stop(); UnregisterMessageHandlers(); SelectionCoordinator.Reset(); HostConfigSynchronizer.Reset(); MeltdownHudController.Destroy(); ApparatusLocatorRegistry.Destroy(); Harmony? harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } private static void EnsureNetworkRegistration() { //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Expected O, but got Unknown //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Expected O, but got Unknown NetworkManager singleton = NetworkManager.Singleton; if (singleton == RegisteredNetworkManager && ((Object)(object)singleton == (Object)null || singleton.IsListening)) { return; } UnregisterMessageHandlers(); SelectionCoordinator.Reset(); HostConfigSynchronizer.Reset(); MeltdownHudController.ResetSession(); if ((Object)(object)singleton == (Object)null || !singleton.IsListening) { return; } try { CustomMessagingManager customMessagingManager = singleton.CustomMessagingManager; object obj = <>O.<0>__HandleSelectionMessage; if (obj == null) { HandleNamedMessageDelegate val = HandleSelectionMessage; <>O.<0>__HandleSelectionMessage = val; obj = (object)val; } customMessagingManager.RegisterNamedMessageHandler("CombinedMeltdownPlaylist.Selection.v2", (HandleNamedMessageDelegate)obj); CustomMessagingManager customMessagingManager2 = singleton.CustomMessagingManager; object obj2 = <>O.<1>__Receive; if (obj2 == null) { HandleNamedMessageDelegate val2 = HostConfigSynchronizer.Receive; <>O.<1>__Receive = val2; obj2 = (object)val2; } customMessagingManager2.RegisterNamedMessageHandler("CombinedMeltdownPlaylist.Config.v2", (HandleNamedMessageDelegate)obj2); RegisteredNetworkManager = singleton; HostConfigSynchronizer.ForceBroadcast(); ModLog.LogDebug((object)"Registered the synchronized playlist and host-config message handlers."); } catch (Exception arg) { ModLog.LogError((object)$"Could not register the synchronization message handlers: {arg}"); } } private static void UnregisterMessageHandlers() { if ((Object)(object)RegisteredNetworkManager == (Object)null) { return; } try { RegisteredNetworkManager.CustomMessagingManager.UnregisterNamedMessageHandler("CombinedMeltdownPlaylist.Selection.v2"); RegisteredNetworkManager.CustomMessagingManager.UnregisterNamedMessageHandler("CombinedMeltdownPlaylist.Config.v2"); } catch (Exception ex) { ModLog.LogDebug((object)("Message handlers were already unavailable: " + ex.Message)); } finally { RegisteredNetworkManager = null; } } private static void HandleSelectionMessage(ulong senderClientId, FastBufferReader reader) { NetworkManager registeredNetworkManager = RegisteredNetworkManager; if ((Object)(object)registeredNetworkManager == (Object)null || registeredNetworkManager.IsServer) { return; } if (senderClientId != 0L) { ModLog.LogWarning((object)$"Ignored a playlist selection from non-host client {senderClientId}."); return; } try { string selectedTrack = default(string); ((FastBufferReader)(ref reader)).ReadValueSafe(ref selectedTrack, true); SelectionCoordinator.ReceiveHostSelection(selectedTrack); } catch (Exception arg) { ModLog.LogError((object)$"Could not read the host's playlist selection: {arg}"); } } internal unsafe static bool BroadcastSelection(string selectedTrack) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) NetworkManager registeredNetworkManager = RegisteredNetworkManager; if ((Object)(object)registeredNetworkManager == (Object)null || !registeredNetworkManager.IsListening || !registeredNetworkManager.IsServer) { return false; } try { FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(512, (Allocator)2, -1); try { ((FastBufferWriter)(ref val)).WriteValueSafe(selectedTrack, true); registeredNetworkManager.CustomMessagingManager.SendNamedMessageToAll("CombinedMeltdownPlaylist.Selection.v2", val, (NetworkDelivery)3); return true; } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } } catch (Exception arg) { ModLog.LogError((object)$"Could not broadcast the playlist selection: {arg}"); return false; } } } [HarmonyPatch(typeof(MusicManager), "LoadMusicClip")] internal static class MusicManagerLoadMusicClipPatch { private static bool Prefix(string musicFileName, ref Task __result) { if (!TrackMetadata.Contains(musicFileName)) { return true; } __result = Task.FromResult(null); return false; } } [HarmonyPatch(typeof(MusicManager), "PlayMusic")] internal static class MusicManagerPlayMusicPatch { private static bool Prefix(MusicManager __instance, Script script, ScriptEvent_PlayMusic musicEvent) { if (!string.Equals(musicEvent.tag, "CombinedMeltdownPlaylist", StringComparison.Ordinal)) { return true; } SelectionCoordinator.Intercept(__instance, script, musicEvent); return false; } } [HarmonyPatch(typeof(MusicManager), "StopMusic")] internal static class MusicManagerStopMusicPatch { private static void Postfix(string? targetTag = null) { if (targetTag == null || string.Equals(targetTag, "CombinedMeltdownPlaylist", StringComparison.Ordinal)) { MeltdownDepartureController.StopImmediatelyOrFade(); } } } [HarmonyPatch(typeof(MusicManager), "FadeStopMusic")] internal static class MusicManagerFadeStopMusicPatch { private static void Postfix(string? targetTag = null) { if (targetTag == null || string.Equals(targetTag, "CombinedMeltdownPlaylist", StringComparison.Ordinal)) { MeltdownDepartureController.StopImmediatelyOrFade(); } } } internal static class SelectionCoordinator { private const float RebroadcastIntervalSeconds = 1.25f; private const float RebroadcastDurationSeconds = 8f; private const float ActiveMeltdownKeepaliveSeconds = 5f; private static readonly List ShuffleBag = new List(); private static string? _lastShuffleSelection; private static string? _hostSelection; private static MusicManager? _clientManager; private static float _nextBroadcastTime; private static float _broadcastUntil; internal static void Intercept(MusicManager manager, Script script, ScriptEvent_PlayMusic musicEvent) { string[] musicNames = musicEvent.musicNames; if (musicNames == null || musicNames.Length == 0) { Plugin.ModLog.LogError((object)"The combined meltdown playlist has no tracks."); return; } NetworkManager registeredNetworkManager = Plugin.RegisteredNetworkManager; if ((Object)(object)registeredNetworkManager == (Object)null || !registeredNetworkManager.IsListening) { string selectedTrack = ChooseTrack(musicNames, playlistMode: false); Plugin.ModLog.LogWarning((object)"Networking was not ready; selecting the meltdown track locally."); StartSelectedTrack(manager, script, musicEvent, selectedTrack); } else if (registeredNetworkManager.IsServer) { string text = ChooseTrack(musicNames, HostConfigSynchronizer.Current.PlaylistMode); HostConfigSynchronizer.ForceBroadcast(); _hostSelection = text; _nextBroadcastTime = Time.unscaledTime + 1.25f; _broadcastUntil = Time.unscaledTime + 8f; if (!Plugin.BroadcastSelection(text)) { Plugin.ModLog.LogWarning((object)"The host selection could not be broadcast; playing it locally."); } Plugin.ModLog.LogInfo((object)("Host selected meltdown track: " + text)); StartSelectedTrack(manager, script, musicEvent, text); } else if (registeredNetworkManager.IsClient) { _clientManager = manager; Plugin.ModLog.LogDebug((object)"Client apparatus event received; waiting for the host's direct track command."); } } internal static void ReceiveHostSelection(string selectedTrack) { if (string.IsNullOrWhiteSpace(selectedTrack)) { Plugin.ModLog.LogWarning((object)"Ignored an empty playlist selection from the host."); } else if (!TrackMetadata.Contains(selectedTrack)) { Plugin.ModLog.LogError((object)("The host selected '" + selectedTrack + "', but it is not in this client's track table. All players must install the same package version.")); } else if (!string.Equals(LazyMusicPlayer.CurrentFilename, selectedTrack, StringComparison.OrdinalIgnoreCase)) { Plugin.ModLog.LogInfo((object)("Streaming host-selected meltdown track: " + selectedTrack)); if ((Object)(object)_clientManager != (Object)null) { _clientManager.StopMusic((string)null); _clientManager = null; } LazyMusicPlayer.PlayNetworkSelection(selectedTrack); } } internal static void Tick() { if (_hostSelection == null || Time.unscaledTime < _nextBroadcastTime) { return; } if (MeltdownDepartureController.IsShipDeparting || !MeltdownAPI.MeltdownStarted || !string.Equals(LazyMusicPlayer.CurrentFilename, _hostSelection, StringComparison.OrdinalIgnoreCase)) { _hostSelection = null; return; } NetworkManager registeredNetworkManager = Plugin.RegisteredNetworkManager; if (!((Object)(object)registeredNetworkManager == (Object)null) && registeredNetworkManager.IsListening && registeredNetworkManager.IsServer) { float num = ((Time.unscaledTime <= _broadcastUntil) ? 1.25f : 5f); _nextBroadcastTime = Time.unscaledTime + num; Plugin.BroadcastSelection(_hostSelection); } } internal static void Reset() { _hostSelection = null; _clientManager = null; _nextBroadcastTime = 0f; _broadcastUntil = 0f; } private static void StartSelectedTrack(MusicManager manager, Script script, ScriptEvent_PlayMusic musicEvent, string selectedTrack) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected I4, but got Unknown OverlapHandling overlapHandling = musicEvent.overlapHandling; switch (overlapHandling - 2) { case 0: manager.StopMusic((string)null); break; case 1: manager.StopMusic(musicEvent.tag); break; case 2: manager.FadeStopMusic((string)null); break; case 3: manager.FadeStopMusic(musicEvent.tag); break; } LazyMusicPlayer.Play(script, musicEvent, selectedTrack); } private static string ChooseTrack(string[] choices, bool playlistMode) { if (!playlistMode) { return choices[Random.Range(0, choices.Length)]; } if (ShuffleBag.Count <= 0 || !ShuffleBag.TrueForAll((string choice) => Array.IndexOf(choices, choice) >= 0)) { ShuffleBag.Clear(); } if (ShuffleBag.Count == 0) { ShuffleBag.AddRange(choices); for (int num = ShuffleBag.Count - 1; num > 0; num--) { int num2 = Random.Range(0, num + 1); List shuffleBag = ShuffleBag; int index = num; List shuffleBag2 = ShuffleBag; int index2 = num2; string value = ShuffleBag[num2]; string value2 = ShuffleBag[num]; shuffleBag[index] = value; shuffleBag2[index2] = value2; } if (ShuffleBag.Count > 1) { List shuffleBag3 = ShuffleBag; if (string.Equals(shuffleBag3[shuffleBag3.Count - 1], _lastShuffleSelection, StringComparison.Ordinal)) { List shuffleBag = ShuffleBag; int index2 = shuffleBag.Count - 1; List shuffleBag4 = ShuffleBag; string value2 = ShuffleBag[0]; List shuffleBag5 = ShuffleBag; string value = shuffleBag5[shuffleBag5.Count - 1]; shuffleBag[index2] = value2; shuffleBag4[0] = value; } } } List shuffleBag6 = ShuffleBag; string text = shuffleBag6[shuffleBag6.Count - 1]; ShuffleBag.RemoveAt(ShuffleBag.Count - 1); _lastShuffleSelection = text; return text; } } internal static class LazyMusicPlayer { private static Coroutine? _loadCoroutine; private static AudioSource? _audioSource; private static AudioClip? _audioClip; private static float _scriptVolume = 1f; private static bool _silenceGameMusic; private static int _playGeneration; private static bool _fadingOut; private static float _fadeStartTime; private static float _fadeDuration; internal static string? CurrentFilename { get; private set; } internal static void Play(Script script, ScriptEvent_PlayMusic musicEvent, string filename) { Stop(); _playGeneration++; int playGeneration = _playGeneration; _scriptVolume = script.TryGetVolumeGroupOrDefault(musicEvent.tag).GetVolume(script); _silenceGameMusic = musicEvent.silenceGameMusic; CurrentFilename = filename; _loadCoroutine = ((MonoBehaviour)Plugin.Instance).StartCoroutine(LoadAndPlay(filename, musicEvent.loop, musicEvent.bypassEffects, playGeneration)); } internal static void PlayNetworkSelection(string filename) { Stop(); _playGeneration++; int playGeneration = _playGeneration; _scriptVolume = 1f; _silenceGameMusic = true; CurrentFilename = filename; _loadCoroutine = ((MonoBehaviour)Plugin.Instance).StartCoroutine(LoadAndPlay(filename, loop: false, bypassEffects: false, playGeneration)); } internal static void Tick() { if ((Object)(object)_audioSource == (Object)null || !_audioSource.isPlaying) { return; } float num = 1f; if (_fadingOut) { float num2 = Mathf.Clamp01((Time.unscaledTime - _fadeStartTime) / _fadeDuration); if (num2 >= 1f) { Stop(); return; } num = 1f - num2; } _audioSource.volume = _scriptVolume * HostConfigSynchronizer.Current.MusicVolume * num; if (_silenceGameMusic) { if ((Object)(object)SoundManager.Instance != (Object)null && SoundManager.Instance.playingOutsideMusic) { SoundManager.Instance.playingOutsideMusic = false; } if ((Object)(object)TimeOfDay.Instance != (Object)null && (Object)(object)TimeOfDay.Instance.TimeOfDayMusic != (Object)null && TimeOfDay.Instance.TimeOfDayMusic.isPlaying) { TimeOfDay.Instance.TimeOfDayMusic.Stop(); } } } internal static void BeginFadeOut(float seconds) { if (!_fadingOut) { if (seconds <= 0f || (Object)(object)_audioSource == (Object)null || !_audioSource.isPlaying) { Stop(); return; } _fadingOut = true; _fadeStartTime = Time.unscaledTime; _fadeDuration = Mathf.Max(0.01f, seconds); Plugin.ModLog.LogDebug((object)$"Fading meltdown music out over {_fadeDuration:0.##} seconds."); } } internal static void Stop() { _playGeneration++; if (_loadCoroutine != null && (Object)(object)Plugin.Instance != (Object)null) { ((MonoBehaviour)Plugin.Instance).StopCoroutine(_loadCoroutine); _loadCoroutine = null; } if ((Object)(object)_audioSource != (Object)null) { _audioSource.Stop(); Object.Destroy((Object)(object)_audioSource); _audioSource = null; } if ((Object)(object)_audioClip != (Object)null) { _audioClip.UnloadAudioData(); Object.Destroy((Object)(object)_audioClip); _audioClip = null; } _silenceGameMusic = false; _fadingOut = false; _fadeStartTime = 0f; _fadeDuration = 0f; CurrentFilename = null; } private static IEnumerator LoadAndPlay(string filename, bool loop, bool bypassEffects, int generation) { string text = ResolveMusicPath(filename); if (text == null) { Plugin.ModLog.LogError((object)("Cannot stream meltdown music because '" + filename + "' was not found beside the DLL or in BGN-PizzaTowerEscapeMusic/Music.")); CurrentFilename = null; yield break; } string absoluteUri = new Uri(text).AbsoluteUri; UnityWebRequest request = UnityWebRequestMultimedia.GetAudioClip(absoluteUri, (AudioType)14); try { DownloadHandler downloadHandler = request.downloadHandler; DownloadHandlerAudioClip val = (DownloadHandlerAudioClip)(object)((downloadHandler is DownloadHandlerAudioClip) ? downloadHandler : null); if (val != null) { val.streamAudio = true; } request.SendWebRequest(); while (!request.isDone) { yield return null; } if (generation != _playGeneration) { yield break; } if ((int)request.result != 1) { Plugin.ModLog.LogError((object)("Could not stream meltdown track '" + filename + "': " + request.error)); yield break; } AudioClip content = DownloadHandlerAudioClip.GetContent(request); if ((Object)(object)content == (Object)null) { Plugin.ModLog.LogError((object)("Streaming returned no audio clip for '" + filename + "'.")); yield break; } ((Object)content).name = filename; _audioClip = content; _audioSource = ((Component)Plugin.Instance).gameObject.AddComponent(); _audioSource.clip = content; _audioSource.loop = loop; _audioSource.bypassEffects = bypassEffects; _audioSource.bypassListenerEffects = bypassEffects; _audioSource.bypassReverbZones = bypassEffects; _audioSource.spatialBlend = 0f; _audioSource.volume = _scriptVolume * HostConfigSynchronizer.Current.MusicVolume; _audioSource.Play(); _loadCoroutine = null; MeltdownHudController.OnTrackStarted(filename); Plugin.ModLog.LogInfo((object)("On-demand stream started for meltdown track: " + filename)); } finally { ((IDisposable)request)?.Dispose(); } } private static string? ResolveMusicPath(string filename) { string text = Path.GetDirectoryName(typeof(Plugin).Assembly.Location) ?? Paths.PluginPath; string[] array = new string[2] { Path.Combine(text, filename), Path.Combine(text, "BGN-PizzaTowerEscapeMusic", "Music", filename) }; foreach (string path in array) { if (File.Exists(path)) { return Path.GetFullPath(path); } } try { using IEnumerator enumerator = Directory.EnumerateFiles(text, filename, SearchOption.AllDirectories).GetEnumerator(); if (enumerator.MoveNext()) { return Path.GetFullPath(enumerator.Current); } } catch (Exception ex) { Plugin.ModLog.LogDebug((object)("Could not search the package directory for '" + filename + "': " + ex.Message)); } return null; } } internal sealed class TrackInfo { internal string Filename { get; } internal string Title { get; } internal string? Author { get; } internal float Bpm { get; } internal float BeatOffsetSeconds { get; } internal bool HasBpm => Bpm > 0f; internal TrackInfo(string filename, string title, string? author, float bpm, float beatOffsetSeconds) { Filename = filename; Title = title; Author = author; Bpm = bpm; BeatOffsetSeconds = beatOffsetSeconds; } } internal static class TrackMetadata { private static readonly Dictionary Tracks = new Dictionary(StringComparer.OrdinalIgnoreCase) { ["grace-meltdown-actions.ogg"] = Track("grace-meltdown-actions.ogg", "ACTION & CONSEQUENCES", "Sebastian Fennec", 186f, 0.046f), ["grace-meltdown-carnation.ogg"] = Track("grace-meltdown-carnation.ogg", "Carnation (CST)", "marshall4", 186f, 0.079f), ["grace-meltdown-clock.ogg"] = Track("grace-meltdown-clock.ogg", "Clocktower Mayhem", "Sebastian Fennec", 150f, 0.148f), ["grace-meltdown-clot.ogg"] = Track("grace-meltdown-clot.ogg", "Bloody Devastation", "Phill C.", 120f, 0.081f), ["grace-meltdown-goob.ogg"] = Track("grace-meltdown-goob.ogg", "For the Greater Goob (CST)", "mich", 186f, 0.14f), ["grace-meltdown-lst.ogg"] = Track("grace-meltdown-lst.ogg", "Mayhem Last Soul Standing", null, 150f, 0.018f), ["grace-meltdown-nede.ogg"] = Track("grace-meltdown-nede.ogg", "SPEAK NO EVIL", "Sebastian Fennec", 162f, 0.151f), ["grace-meltdown.ogg"] = Track("grace-meltdown.ogg", "ULTRASONIC FAITH", "Phill C.", 188f, 0.117f), ["labirhin-apa-makna-nya.ogg"] = Track("labirhin-apa-makna-nya.ogg", "ApaMaknaNya", "Labirhin", 150f, 0.073f), ["labirhin-breakdown.ogg"] = Track("labirhin-breakdown.ogg", "BREAKDOWN", "Labirhin", 150f, 0.073f), ["labirhin-deleted-message.ogg"] = Track("labirhin-deleted-message.ogg", "DeletedMessage", "Labirhin", 150f, 0.08f), ["labirhin-here-for-nothing.ogg"] = Track("labirhin-here-for-nothing.ogg", "HereForNothing", "Labirhin", 150f, 0.12f), ["labirhin-hurt-overlay.ogg"] = Track("labirhin-hurt-overlay.ogg", "HurtOverlay", "Labirhin", 150f, 0.075f), ["labirhin-kesuniya.ogg"] = Track("labirhin-kesuniya.ogg", "KESUNIYA", "Labirhin", 150f, 0.078f), ["labirhin-need-someone-like-u.ogg"] = Track("labirhin-need-someone-like-u.ogg", "NeedSomeoneLikeU", "Labirhin", 150f, 0.323f), ["labirhin-pulang.ogg"] = Track("labirhin-pulang.ogg", "PULANG", "Labirhin", 150f, 0.073f), ["labirhin-tsaf.ogg"] = Track("labirhin-tsaf.ogg", "TSAF", "Labirhin", 150f, 0.078f), ["meltdown-072-attack-of-the-killer-queen.ogg"] = Track("meltdown-072-attack-of-the-killer-queen.ogg", "Attack of the Killer Queen", "Toby Fox, Lena Raine & Marcy Nabors", 144f, 0.04f), ["meltdown-cool-silly-idea.ogg"] = Track("meltdown-cool-silly-idea.ogg", "just a cool silly little idea that I'm gonna keep", null, 171f, 0.193f), ["meltdown-crime-with-master.ogg"] = Track("meltdown-crime-with-master.ogg", "crime with master", null, 168f, 0.189f), ["meltdown-is.ogg"] = Track("meltdown-is.ogg", "IS", null, 195f, 0.044f), ["meltdown-scp-foundation.ogg"] = Track("meltdown-scp-foundation.ogg", "SCP Foundation", null, 160f, 0.045f), ["meltdown-sg-vocal-mix.ogg"] = Track("meltdown-sg-vocal-mix.ogg", "SG - vocal mix", null, 188f, 0.094f), ["meltdown-sh4dy-gr3y-1one.ogg"] = Track("meltdown-sh4dy-gr3y-1one.ogg", "SH4DY-GR3Y - 1One", null, 180f, 0.046f), ["meltdown-shady-grey-3three.ogg"] = Track("meltdown-shady-grey-3three.ogg", "Shady Grey - 3Three", null, 160f, 0.042f), ["meltdown-wwii-moment.ogg"] = Track("meltdown-wwii-moment.ogg", "wwii moment i think", null, 165f, 0.171f), ["run-for-it.ogg"] = Track("run-for-it.ogg", "RUN FOR IT", "insOmic", 180f, 0.167f), ["workspace-079-big-shot.ogg"] = Track("workspace-079-big-shot.ogg", "BIG SHOT", "Toby Fox", 140f, 0.261f), ["workspace-114-its-tv-time.ogg"] = Track("workspace-114-its-tv-time.ogg", "It's TV Time!", "Toby Fox", 148f, 0.255f), ["workspace-117-black-knife.ogg"] = Track("workspace-117-black-knife.ogg", "Black Knife", "Toby Fox", 147.5f, 0.258f), ["workspace-155-guardian.ogg"] = Track("workspace-155-guardian.ogg", "GUARDIAN", "Toby Fox", 140f, 0.054f), ["workspace-199-flower-man.ogg"] = Track("workspace-199-flower-man.ogg", "Flower Man", "Toby Fox", 160f, 0.23f), ["workspace-hotel-chase.ogg"] = Track("workspace-hotel-chase.ogg", "Hotel Chase", null, 130f, 0.354f), ["workspace-level-soundtrack-remastered.ogg"] = Track("workspace-level-soundtrack-remastered.ogg", "Level ! But It's Actually a Soundtrack REMASTERED", "Dapper Husky", 155f, 0.105f), ["workspace-off-with-ya-head.ogg"] = Track("workspace-off-with-ya-head.ogg", "OFF WITH YA HEAD!", "googifloop", 180f, 0.153f), ["workspace-restore-point-interceptor.ogg"] = Track("workspace-restore-point-interceptor.ogg", "Restore Point Interceptor", null, 175f, 0.271f), ["import-three-cheers-for-cause-and-effect.ogg"] = Track("import-three-cheers-for-cause-and-effect.ogg", "Three Cheers for Cause and Effect", "Arsi \"Hakita\" Patala", 108f, 0.604f), ["import-10-hour-s-worst-man.ogg"] = Track("import-10-hour-s-worst-man.ogg", "10 Hour's Worst Man", "Aden Mayo", 90f, 4.714f), ["import-10-hours.ogg"] = Track("import-10-hours.ogg", "10 Hours", "Aden Mayo", 100f, 0.186f), ["import-a-complete-and-utter-destruction-of-the-senses.ogg"] = Track("import-a-complete-and-utter-destruction-of-the-senses.ogg", "A Complete and Utter Destruction of the Senses", "Heaven Pierce Her", 160f, 27.725f), ["import-asgore.ogg"] = Track("import-asgore.ogg", "ASGORE", "Toby Fox", 230f, 0.557f), ["import-altars-of-apostasy.ogg"] = Track("import-altars-of-apostasy.ogg", "Altars of Apostasy", "Heaven Pierce Her", 150f, 0.093f), ["import-battle-against-a-true-hero.ogg"] = Track("import-battle-against-a-true-hero.ogg", "Battle Against a True Hero", "Toby Fox", 300f, 0.348f), ["import-bull-of-hell.ogg"] = Track("import-bull-of-hell.ogg", "Bull of Hell", "Heaven Pierce Her", 100f, 0.906f), ["import-chaos.ogg"] = Track("import-chaos.ogg", "CHAOS", "Heaven Pierce Her", 120f, 2.067f), ["import-chaos-king.ogg"] = Track("import-chaos-king.ogg", "Chaos King", "Toby Fox", 200f, 0.65f), ["import-confessions-of-a-rotten-girl.ogg"] = Track("import-confessions-of-a-rotten-girl.ogg", "Confessions of a Rotten Girl", "SAWTOWNE", 170f, 1.486f), ["import-danse-macabre.ogg"] = Track("import-danse-macabre.ogg", "Danse Macabre", "Heaven Pierce Her", 160f, 0.07f), ["import-dead-heat-pulse-a-heart-of-cold.ogg"] = Track("import-dead-heat-pulse-a-heart-of-cold.ogg", "Dead Heat Pulse (A Heart of Cold)", "Heaven Pierce Her", 136f, 0.906f), ["import-death-odyssey.ogg"] = Track("import-death-odyssey.ogg", "Death Odyssey", "Heaven Pierce Her", 190f, 0.209f), ["import-deep-blue.ogg"] = Track("import-deep-blue.ogg", "Deep Blue", "Heaven Pierce Her", 180f, 0.557f), ["import-divine-intervention.ogg"] = Track("import-divine-intervention.ogg", "Divine Intervention", "Heaven Pierce Her", 170f, 0.07f), ["import-duel-versus-reprise.ogg"] = Track("import-duel-versus-reprise.ogg", "Duel (Versus Reprise)", "Heaven Pierce Her", 120f, 0.557f), ["import-dune-eternal.ogg"] = Track("import-dune-eternal.ogg", "Dune Eternal", "Heaven Pierce Her", 140f, 0.464f), ["import-echo-instrumental.ogg"] = Track("import-echo-instrumental.ogg", "ECHO (Instrumental)", "ReAdmission", 144f, 0.093f), ["import-echo.ogg"] = Track("import-echo.ogg", "ECHO", "ReAdmission", 144f, 0.093f), ["import-emergency.ogg"] = Track("import-emergency.ogg", "EMERGENCY", "P*Light", 145f, 13.282f), ["import-finale.ogg"] = Track("import-finale.ogg", "Finale", "Toby Fox", 190f, 0.07f), ["import-flux-in-a-box.ogg"] = Track("import-flux-in-a-box.ogg", "Flux in a Box", "Arsi \"Hakita\" Patala", 108f, 0.627f), ["import-fun-with-teeth-bye-bye-pt-2.ogg"] = Track("import-fun-with-teeth-bye-bye-pt-2.ogg", "Fun With Teeth (Bye-Bye, Pt. 2)", "Heaven Pierce Her", 108f, 0.604f), ["import-halls-of-sacreligious-remains.ogg"] = Track("import-halls-of-sacreligious-remains.ogg", "Halls of Sacreligious Remains", "Heaven Pierce Her", 150f, 2.345f), ["import-halls-of-treachery.ogg"] = Track("import-halls-of-treachery.ogg", "Halls of Treachery", "Dapper Husky", 108f, 0.139f), ["import-he-is-the-light-in-my-darkness.ogg"] = Track("import-he-is-the-light-in-my-darkness.ogg", "He Is the Light in My Darkness", "Heaven Pierce Her", 95f, 0.07f), ["import-heart-ripper.ogg"] = Track("import-heart-ripper.ogg", "Heart Ripper", "Vbuh", 112f, 0.07f), ["import-infinite-jest.ogg"] = Track("import-infinite-jest.ogg", "Infinite Jest", "Arsi \"Hakita\" Patala", 108f, 2.299f), ["import-into-the-fire.ogg"] = Track("import-into-the-fire.ogg", "Into the Fire", "Heaven Pierce Her", 160f, 0.07f), ["import-it-s-tv-time.ogg"] = Track("import-it-s-tv-time.ogg", "It's TV Time!", "Toby Fox", 148f, 0.209f), ["import-kakusei-syndrome.ogg"] = Track("import-kakusei-syndrome.ogg", "KAKUSEI SYNDROME", "Getty", 152f, 1.254f), ["import-l0st-h0p3.ogg"] = Track("import-l0st-h0p3.ogg", "L0ST H0P3", "Dapper Husky", 120f, 2.043f), ["import-level-but-it-s-a-retro-remix.ogg"] = Track("import-level-but-it-s-a-retro-remix.ogg", "Level ! But It's a Retro Remix", "Dapper Husky", 96f, 0.209f), ["import-level-6-polluted-nature.ogg"] = Track("import-level-6-polluted-nature.ogg", "Level -6 - Polluted Nature", "Dapper Husky", 130f, 9.033f), ["import-megalovania.ogg"] = Track("import-megalovania.ogg", "MEGALOVANIA", "Toby Fox", 240f, 0.534f), ["import-mirror-rim.ogg"] = Track("import-mirror-rim.ogg", "Mirror Rim", "Heaven Pierce Her", 120f, 1.045f), ["import-noisyverse.ogg"] = Track("import-noisyverse.ogg", "NOISYVERSE", "Kobaryo", 200f, 0.627f), ["import-no-devil-lived-on.ogg"] = Track("import-no-devil-lived-on.ogg", "No Devil Lived On", "Heaven Pierce Her", 123f, 1.022f), ["import-order.ogg"] = Track("import-order.ogg", "ORDER", "Heaven Pierce Her", 170f, 0.58f), ["import-pandemonium.ogg"] = Track("import-pandemonium.ogg", "PANDEMONIUM", "Heaven Pierce Her", 160f, 0.232f), ["import-panic-betrayer.ogg"] = Track("import-panic-betrayer.ogg", "Panic Betrayer", "Heaven Pierce Her", 194f, 0.511f), ["import-smiler.ogg"] = Track("import-smiler.ogg", "SMILER", "Aden Mayo", 112f, 0.163f), ["import-sands-of-tide.ogg"] = Track("import-sands-of-tide.ogg", "Sands of Tide", "Heaven Pierce Her", 150f, 0.07f), ["import-second-trumpet.ogg"] = Track("import-second-trumpet.ogg", "Second Trumpet", "Aden Mayo", 90f, 0.116f), ["import-silent-note.ogg"] = Track("import-silent-note.ogg", "Silent Note", "Aden Mayo", 100f, 0.07f), ["import-spiral-out-keep-going.ogg"] = Track("import-spiral-out-keep-going.ogg", "Spiral Out (Keep Going)", "Heaven Pierce Her", 152f, 2.856f), ["import-tenebre-rosso-sangue.ogg"] = Track("import-tenebre-rosso-sangue.ogg", "Tenebre Rosso Sangue", "KEYGEN CHURCH", 176f, 0.557f), ["import-the-american-century-as-seen-through-a-brick.ogg"] = Track("import-the-american-century-as-seen-through-a-brick.ogg", "The American Century as Seen Through a Brick", "Heaven Pierce Her", 108f, 0.07f), ["import-the-break-crimson-glass-decomposition.ogg"] = Track("import-the-break-crimson-glass-decomposition.ogg", "The Break (Crimson Glass deComposition)", "Heaven Pierce Her", 108f, 0.604f), ["import-the-cyber-grind.ogg"] = Track("import-the-cyber-grind.ogg", "The Cyber Grind", "meganeko", 190f, 0.673f), ["import-the-death-of-god-s-will.ogg"] = Track("import-the-death-of-god-s-will.ogg", "The Death of God's Will", "Heaven Pierce Her", 160f, 0.789f), ["import-the-shattering-circle.ogg"] = Track("import-the-shattering-circle.ogg", "The Shattering Circle", "Heaven Pierce Her", 120f, 2.554f), ["import-twilight.ogg"] = Track("import-twilight.ogg", "Twilight", "Aden Mayo", 96f, 6.432f), ["import-unstoppable-force.ogg"] = Track("import-unstoppable-force.ogg", "Unstoppable Force", "Heaven Pierce Her", 174f, 0.07f), ["import-versus.ogg"] = Track("import-versus.ogg", "Versus", "Heaven Pierce Her", 120f, 0.557f), ["import-war.ogg"] = Track("import-war.ogg", "WAR", "Heaven Pierce Her", 175f, 0.557f), ["import-war-without-reason-timed-edit.ogg"] = Track("import-war-without-reason-timed-edit.ogg", "War Without Reason (Timed Edit)", "Heaven Pierce Her", 120f, 0.557f), ["import-war-without-reason.ogg"] = Track("import-war-without-reason.ogg", "War Without Reason", "Heaven Pierce Her", 120f, 16.022f), ["import-wave-bye-bye-to-the-bureaucrat.ogg"] = Track("import-wave-bye-bye-to-the-bureaucrat.ogg", "Wave Bye-Bye to the Bureaucrat", "Arsi \"Hakita\" Patala", 108f, 0.882f), ["import-your-best-nightmare.ogg"] = Track("import-your-best-nightmare.ogg", "Your Best Nightmare", "Toby Fox", 190f, 0.093f), ["import-storm.ogg"] = Track("import-storm.ogg", "storm", null, 130f, 0.418f), ["import-i-m-talking-to-you.ogg"] = Track("import-i-m-talking-to-you.ogg", "I'm Talking To You!", "sansansanxu__", 90f, 1.718f) }; internal static int Count => Tracks.Count; internal static bool Contains(string filename) { return Tracks.ContainsKey(filename); } internal static TrackInfo Resolve(string filename) { if (Tracks.TryGetValue(filename, out TrackInfo value)) { return value; } string text = Path.GetFileNameWithoutExtension(filename); string[] array = new string[4] { "workspace-", "meltdown-", "labirhin-", "grace-" }; foreach (string text2 in array) { if (text.StartsWith(text2, StringComparison.OrdinalIgnoreCase)) { text = text.Substring(text2.Length); break; } } text = text.Replace('-', ' ').Replace('_', ' ').Trim(); text = CultureInfo.InvariantCulture.TextInfo.ToTitleCase(text); return Track(filename, text, null, 0f, 0f); } private static TrackInfo Track(string filename, string title, string? author, float bpm, float beatOffsetSeconds) { return new TrackInfo(filename, title, author, bpm, beatOffsetSeconds); } } internal static class VcrFontFactory { private const string ResourceName = "CombinedMeltdownPlaylistSync.Assets.VCR_OSD_MONO_1.001.ttf"; private const int PointSize = 90; private const int AtlasSize = 1024; private const int Padding = 8; private const string IncludedCharacters = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; private static TMP_FontAsset? _fontAsset; private static Texture2D? _atlasTexture; private static Material? _material; private static bool _ownsFontAsset; internal static TMP_FontAsset GetOrCreate() { if ((Object)(object)_fontAsset != (Object)null) { return _fontAsset; } try { _fontAsset = BuildFontAsset(); _ownsFontAsset = true; return _fontAsset; } catch (Exception arg) { Plugin.ModLog.LogError((object)$"Could not create the embedded VCR OSD Mono HUD font: {arg}"); HUDManager instance = HUDManager.Instance; object obj; if (instance == null) { obj = null; } else { TextMeshProUGUI clockNumber = instance.clockNumber; obj = ((clockNumber != null) ? ((TMP_Text)clockNumber).font : null); } if ((Object)obj == (Object)null) { throw; } Plugin.ModLog.LogWarning((object)"Using the vanilla clock font as a HUD fallback."); _fontAsset = (TMP_FontAsset?)obj; _ownsFontAsset = false; return (TMP_FontAsset)obj; } } internal static void Destroy() { if ((Object)(object)_fontAsset != (Object)null && _ownsFontAsset) { Object.Destroy((Object)(object)_fontAsset); } if ((Object)(object)_material != (Object)null) { Object.Destroy((Object)(object)_material); } if ((Object)(object)_atlasTexture != (Object)null) { Object.Destroy((Object)(object)_atlasTexture); } _fontAsset = null; _material = null; _atlasTexture = null; _ownsFontAsset = false; } private static TMP_FontAsset BuildFontAsset() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_046a: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: 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) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Expected O, but got Unknown //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_0269: Unknown result type (might be due to invalid IL or missing references) //IL_0273: Expected O, but got Unknown //IL_02aa: Unknown result type (might be due to invalid IL or missing references) //IL_03ad: Unknown result type (might be due to invalid IL or missing references) //IL_03b2: Unknown result type (might be due to invalid IL or missing references) //IL_03bd: Unknown result type (might be due to invalid IL or missing references) //IL_03ca: Expected O, but got Unknown byte[] array = ReadEmbeddedFont(); FontEngineError val = FontEngine.InitializeFontEngine(); if ((int)val != 0) { throw new InvalidOperationException($"FontEngine initialization failed: {val}"); } FontEngineError val2 = FontEngine.LoadFontFace(array, 90, 0); if ((int)val2 != 0) { throw new InvalidOperationException($"VCR OSD Mono could not be loaded from embedded bytes: {val2}"); } try { List list = new List(" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~".Length); List list2 = new List(" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~".Length); Dictionary dictionary = new Dictionary(); HashSet hashSet = new HashSet(); string text = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; Glyph val3 = default(Glyph); foreach (uint num in text) { if (FontEngine.TryGetGlyphWithUnicodeValue(num, (GlyphLoadFlags)0, ref val3) && val3.index != 0) { uint index = val3.index; list.Add(num); dictionary[num] = index; if (hashSet.Add(index)) { list2.Add(index); } } } _atlasTexture = new Texture2D(1024, 1024, (TextureFormat)1, false) { name = "VCR OSD Mono Atlas", hideFlags = (HideFlags)61 }; InvokeInternalFontEngineMethod("ResetAtlasTexture", new object[1] { _atlasTexture }); List list3 = new List { new GlyphRect(0, 0, 1023, 1023) }; List list4 = new List(); object[] array2 = new object[8] { list2, 8, (object)(GlyphPackingMode)0, list3, list4, (object)(GlyphRenderMode)4165, _atlasTexture, null }; bool num2 = (bool)InvokeInternalFontEngineMethod("TryAddGlyphsToTexture", array2, 8, typeof(List)); Glyph[] array3 = ((Glyph[])array2[7]) ?? Array.Empty(); if (!num2) { throw new InvalidOperationException("VCR OSD Mono glyphs did not fit in the runtime atlas."); } _atlasTexture.Apply(false, false); List list5 = new List(array3.Length); Dictionary dictionary2 = new Dictionary(array3.Length); Glyph[] array4 = array3; foreach (Glyph val4 in array4) { if (val4 != null) { list5.Add(val4); dictionary2[val4.index] = val4; } } List list6 = new List(list.Count); foreach (uint item in list) { if (dictionary2.TryGetValue(dictionary[item], out var value)) { list6.Add(new TMP_Character(item, value)); } } TMP_FontAsset val5 = ScriptableObject.CreateInstance(); ((Object)val5).name = "VCR OSD Mono"; ((Object)val5).hideFlags = (HideFlags)61; val5.faceInfo = FontEngine.GetFaceInfo(); val5.atlasTextures = (Texture2D[])(object)new Texture2D[1] { _atlasTexture }; val5.atlasPopulationMode = (AtlasPopulationMode)0; val5.isMultiAtlasTexturesEnabled = false; SetPrivateField(val5, "m_Version", "1.1.0"); SetPrivateField(val5, "m_GlyphTable", list5); SetPrivateField(val5, "m_CharacterTable", list6); SetPrivateField(val5, "m_AtlasWidth", 1024); SetPrivateField(val5, "m_AtlasHeight", 1024); SetPrivateField(val5, "m_AtlasPadding", 8); SetPrivateField(val5, "m_AtlasRenderMode", (GlyphRenderMode)4165); SetPrivateField(val5, "m_UsedGlyphRects", list4); SetPrivateField(val5, "m_FreeGlyphRects", list3); HUDManager instance = HUDManager.Instance; object obj; if (instance == null) { obj = null; } else { TextMeshProUGUI clockNumber = instance.clockNumber; if (clockNumber == null) { obj = null; } else { Material fontMaterial = ((TMP_Text)clockNumber).fontMaterial; obj = ((fontMaterial != null) ? fontMaterial.shader : null); } } if (obj == null) { obj = Shader.Find("TextMeshPro/Distance Field"); } if ((Object)obj == (Object)null) { throw new InvalidOperationException("The TextMeshPro distance-field shader was not available."); } _material = new Material((Shader)obj) { name = "VCR OSD Mono Material", hideFlags = (HideFlags)61 }; _material.SetTexture("_MainTex", (Texture)(object)_atlasTexture); _material.SetFloat("_TextureWidth", 1024f); _material.SetFloat("_TextureHeight", 1024f); _material.SetFloat("_GradientScale", 9f); _material.SetFloat("_WeightNormal", 0f); _material.SetFloat("_WeightBold", 0.5f); ((TMP_Asset)val5).material = _material; val5.ReadFontAssetDefinition(); Plugin.ModLog.LogInfo((object)"Loaded embedded VCR OSD Mono HUD font."); return val5; } finally { FontEngine.UnloadFontFace(); } } private static byte[] ReadEmbeddedFont() { using Stream stream = typeof(VcrFontFactory).Assembly.GetManifestResourceStream("CombinedMeltdownPlaylistSync.Assets.VCR_OSD_MONO_1.001.ttf"); if (stream == null) { throw new FileNotFoundException("Embedded font resource 'CombinedMeltdownPlaylistSync.Assets.VCR_OSD_MONO_1.001.ttf' was not found."); } using MemoryStream memoryStream = new MemoryStream(); stream.CopyTo(memoryStream); return memoryStream.ToArray(); } private static object InvokeInternalFontEngineMethod(string name, object?[] arguments, int? parameterCount = null, Type? firstParameterType = null) { MethodInfo methodInfo = null; MethodInfo[] methods = typeof(FontEngine).GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo2 in methods) { ParameterInfo[] parameters = methodInfo2.GetParameters(); if (string.Equals(methodInfo2.Name, name, StringComparison.Ordinal) && (!parameterCount.HasValue || parameters.Length == parameterCount.Value) && (!(firstParameterType != null) || (parameters.Length != 0 && !(parameters[0].ParameterType != firstParameterType)))) { methodInfo = methodInfo2; break; } } if (methodInfo == null) { throw new MissingMethodException(typeof(FontEngine).FullName, name); } object obj = methodInfo.Invoke(null, arguments); if (methodInfo.ReturnType == typeof(void)) { return true; } return obj ?? throw new InvalidOperationException("Internal FontEngine method '" + name + "' returned null."); } private static void SetPrivateField(TMP_FontAsset fontAsset, string fieldName, T value) { FieldInfo fieldInfo = AccessTools.Field(typeof(TMP_FontAsset), fieldName); if (fieldInfo == null) { throw new MissingFieldException(typeof(TMP_FontAsset).FullName, fieldName); } fieldInfo.SetValue(fontAsset, value); } } }