Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of RoundTimer v1.0.0
RoundTimer.dll
Decompiled an hour agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.SceneManagement; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("RoundTimer")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("RoundTimer")] [assembly: AssemblyTitle("RoundTimer")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace RoundTimer { [BepInPlugin("com.splinez.repo.muchbetterroundtimer", "Round Timer", "1.0.0")] public class Plugin : BaseUnityPlugin { public const string PluginGuid = "com.splinez.repo.muchbetterroundtimer"; public const string PluginName = "Round Timer"; public const string PluginVersion = "1.0.0"; internal static ManualLogSource Log; private ConfigEntry<bool> _enabled; private ConfigEntry<bool> _showDecimals; private ConfigEntry<bool> _showWhenIdle; private ConfigEntry<bool> _blendWithHud; private ConfigEntry<bool> _useGameMaterial; private ConfigEntry<bool> _logCanvases; private ConfigEntry<string> _hudCanvasName; private ConfigEntry<float> _fontSize; private ConfigEntry<float> _offsetX; private ConfigEntry<float> _offsetY; private ConfigEntry<Color> _color; private Canvas _fallbackCanvas; private TextMeshProUGUI _text; private TMP_FontAsset _font; private Material _gameMaterial; private bool _attachedToHud; private float _elapsed; private bool _inRound; private float _nextHudTry; private int _lastScreenHeight; private string _lastCanvasLog; private void Awake() { //IL_0140: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; _enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Turn the timer on or off."); _showDecimals = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ShowDecimals", false, "Show hundredths of a second (MM:SS.cc). If off, shows MM:SS."); _showWhenIdle = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ShowWhenIdle", false, "If true, shows 00:00 between rounds instead of hiding the timer."); _blendWithHud = ((BaseUnityPlugin)this).Config.Bind<bool>("Look", "BlendWithGameHud", true, "Attach the timer to the game's own HUD canvas so it gets the same post-processing (chromatic aberration, distortion, etc.) as the rest of the UI."); _useGameMaterial = ((BaseUnityPlugin)this).Config.Bind<bool>("Look", "UseGameTextMaterial", true, "Copy the material (glow/outline/shader) from the game's HUD text."); _hudCanvasName = ((BaseUnityPlugin)this).Config.Bind<string>("Look", "HudCanvasName", "", "Optional: exact name of the canvas to attach to (see the BepInEx log, which lists all canvases once per launch). Leave empty for auto-detection."); _logCanvases = ((BaseUnityPlugin)this).Config.Bind<bool>("Look", "LogCanvases", true, "Log all UI canvases (name, render mode, camera) once, to help pick the right HUD canvas."); _fontSize = ((BaseUnityPlugin)this).Config.Bind<float>("Look", "FontSize", 84f, "Font size (relative to a 1080p-tall canvas; scales with the HUD)."); _color = ((BaseUnityPlugin)this).Config.Bind<Color>("Look", "Color", new Color(1f, 0.3f, 0.01f, 1f), "Text color (default is the orange-red of the game's HUD text, #FF4D03)."); _offsetX = ((BaseUnityPlugin)this).Config.Bind<float>("Position", "OffsetFromRight", 58f, "Distance from the right edge of the screen (text is right-aligned)."); _offsetY = ((BaseUnityPlugin)this).Config.Bind<float>("Position", "OffsetFromTop", 245f, "Distance from the top edge of the screen (default sits under the extraction counter)."); BuildFallbackCanvas(); CreateText(); ((BaseUnityPlugin)this).Config.SettingChanged += delegate { ApplyStyle(); }; Log.LogInfo((object)"Round Timer 1.0.0 loaded."); } private void BuildFallbackCanvas() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_0051: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("RoundTimerCanvas"); Object.DontDestroyOnLoad((Object)(object)val); _fallbackCanvas = val.AddComponent<Canvas>(); _fallbackCanvas.renderMode = (RenderMode)0; _fallbackCanvas.sortingOrder = 500; CanvasScaler obj = val.AddComponent<CanvasScaler>(); obj.uiScaleMode = (ScaleMode)1; obj.referenceResolution = new Vector2(1920f, 1080f); obj.matchWidthOrHeight = 0.5f; } private void CreateText() { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_fallbackCanvas == (Object)null) { BuildFallbackCanvas(); } GameObject val = new GameObject("RoundTimerText"); val.layer = ((Component)_fallbackCanvas).gameObject.layer; val.transform.SetParent(((Component)_fallbackCanvas).transform, false); _text = val.AddComponent<TextMeshProUGUI>(); ((TMP_Text)_text).alignment = (TextAlignmentOptions)516; ((Graphic)_text).raycastTarget = false; ((TMP_Text)_text).enableWordWrapping = false; ((TMP_Text)_text).text = string.Empty; ((Behaviour)_text).enabled = false; RectTransform rectTransform = ((TMP_Text)_text).rectTransform; rectTransform.anchorMin = new Vector2(1f, 1f); rectTransform.anchorMax = new Vector2(1f, 1f); rectTransform.pivot = new Vector2(1f, 1f); _attachedToHud = false; ApplyStyle(); } private void ApplyStyle() { //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: 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_003a: 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_004f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_text == (Object)null) { return; } float num = 1f; Transform parent = ((TMP_Text)_text).transform.parent; RectTransform val = (RectTransform)(object)((parent is RectTransform) ? parent : null); if ((Object)(object)val != (Object)null) { Rect rect = val.rect; if (((Rect)(ref rect)).height > 1f) { rect = val.rect; num = ((Rect)(ref rect)).height / 1080f; } } ((TMP_Text)_text).fontSize = _fontSize.Value * num; ((Graphic)_text).color = _color.Value; RectTransform rectTransform = ((TMP_Text)_text).rectTransform; rectTransform.anchoredPosition = new Vector2((0f - _offsetX.Value) * num, (0f - _offsetY.Value) * num); rectTransform.sizeDelta = new Vector2(500f * num, 100f * num); if ((Object)(object)_font != (Object)null) { ((TMP_Text)_text).font = _font; Material val2 = ((_attachedToHud && _useGameMaterial.Value && (Object)(object)_gameMaterial != (Object)null) ? _gameMaterial : ((TMP_Asset)_font).material); if ((Object)(object)((TMP_Text)_text).fontSharedMaterial != (Object)(object)val2) { ((TMP_Text)_text).fontSharedMaterial = val2; } } _lastScreenHeight = Screen.height; } private void Update() { if ((Object)(object)_text == (Object)null) { CreateText(); } if (!_enabled.Value) { ((Behaviour)_text).enabled = false; return; } bool flag = IsInRound(); if (flag != _inRound) { _elapsed = 0f; _inRound = flag; } if (flag) { _elapsed += Time.deltaTime; MaybeAttachToHud(); } if (!_blendWithHud.Value && _attachedToHud) { DetachFromHud(); } if (_attachedToHud && ((Object)(object)((Graphic)_text).canvas == (Object)null || !((Component)((Graphic)_text).canvas).gameObject.activeInHierarchy)) { _attachedToHud = false; } bool flag2 = flag || _showWhenIdle.Value; ((Behaviour)_text).enabled = flag2; if (flag2) { ((TMP_Text)_text).text = Format(_elapsed); } if (Screen.height != _lastScreenHeight) { ApplyStyle(); } } private static bool IsInRound() { try { if (!SemiFunc.RunIsLevel()) { return false; } LevelGenerator instance = LevelGenerator.Instance; return (Object)(object)instance != (Object)null && instance.Generated; } catch { return false; } } private string Format(float seconds) { int num = (int)(seconds / 60f); float num2 = seconds - (float)num * 60f; if (!_showDecimals.Value) { return $"{num:00}:{(int)num2:00}"; } return $"{num:00}:{num2:00.00}"; } private void MaybeAttachToHud() { //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) if (!_blendWithHud.Value || _attachedToHud || Time.unscaledTime < _nextHudTry) { return; } _nextHudTry = Time.unscaledTime + 1f; if (TryFindHud(out var canvas, out var font, out var material)) { _font = font; _gameMaterial = material; ((TMP_Text)_text).transform.SetParent(((Component)canvas).transform, false); SetLayerRecursive(((Component)_text).gameObject, ((Component)canvas).gameObject.layer); ((TMP_Text)_text).transform.SetAsLastSibling(); _attachedToHud = true; string text = (((Object)(object)canvas.worldCamera != (Object)null) ? ((Object)canvas.worldCamera).name : "none"); Log.LogInfo((object)($"Attached to HUD canvas '{((Object)canvas).name}' (mode: {canvas.renderMode}, " + "camera: " + text + ", layer: " + LayerMask.LayerToName(((Component)canvas).gameObject.layer) + "), font: " + ((Object)font).name + ", material: " + (((Object)(object)material != (Object)null) ? ((Object)material).name : "none"))); if ((int)canvas.renderMode == 0) { Log.LogWarning((object)"The chosen canvas is Screen Space - Overlay, which can't receive post-processing. If the timer still looks clean, set Look/HudCanvasName to a ScreenSpaceCamera/WorldSpace canvas from the canvas list above."); } ApplyStyle(); } } private void DetachFromHud() { if (!((Object)(object)_fallbackCanvas == (Object)null) && !((Object)(object)_text == (Object)null)) { ((TMP_Text)_text).transform.SetParent(((Component)_fallbackCanvas).transform, false); SetLayerRecursive(((Component)_text).gameObject, ((Component)_fallbackCanvas).gameObject.layer); _attachedToHud = false; ApplyStyle(); } } private static void SetLayerRecursive(GameObject go, int layer) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) go.layer = layer; foreach (Transform item in go.transform) { SetLayerRecursive(((Component)item).gameObject, layer); } } private bool TryFindHud(out Canvas canvas, out TMP_FontAsset font, out Material material) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_027b: Unknown result type (might be due to invalid IL or missing references) canvas = null; font = null; material = null; Dictionary<Canvas, List<TMP_Text>> dictionary = new Dictionary<Canvas, List<TMP_Text>>(); TMP_Text[] array = Resources.FindObjectsOfTypeAll<TMP_Text>(); foreach (TMP_Text val in array) { if ((Object)(object)val == (Object)null || (Object)(object)val == (Object)(object)_text) { continue; } Scene scene = ((Component)val).gameObject.scene; if (!((Scene)(ref scene)).IsValid() || !((Component)val).gameObject.activeInHierarchy || (Object)(object)val.font == (Object)null) { continue; } Canvas canvas2 = ((Graphic)val).canvas; if ((Object)(object)canvas2 == (Object)null) { continue; } Canvas rootCanvas = canvas2.rootCanvas; if (!((Object)(object)rootCanvas == (Object)null) && !((Object)(object)rootCanvas == (Object)(object)_fallbackCanvas)) { if (!dictionary.TryGetValue(rootCanvas, out var value)) { value = (dictionary[rootCanvas] = new List<TMP_Text>()); } value.Add(val); } } if (dictionary.Count == 0) { return false; } if (_logCanvases.Value) { StringBuilder stringBuilder = new StringBuilder("UI canvases found:"); foreach (KeyValuePair<Canvas, List<TMP_Text>> item in dictionary) { Canvas key = item.Key; stringBuilder.Append($"\n '{((Object)key).name}' mode={key.renderMode} texts={item.Value.Count} " + "camera=" + (((Object)(object)key.worldCamera != (Object)null) ? ((Object)key.worldCamera).name : "none") + " layer=" + LayerMask.LayerToName(((Component)key).gameObject.layer)); } string text = stringBuilder.ToString(); if (text != _lastCanvasLog) { _lastCanvasLog = text; Log.LogInfo((object)text); } } string value2 = _hudCanvasName.Value; int num = int.MinValue; foreach (KeyValuePair<Canvas, List<TMP_Text>> item2 in dictionary) { Canvas key2 = item2.Key; int num2 = item2.Value.Count; if (!string.IsNullOrEmpty(value2) && ((Object)key2).name == value2) { num2 += 100000; } if (((Object)key2).name.IndexOf("HUD", StringComparison.OrdinalIgnoreCase) >= 0) { num2 += 1000; } if ((int)key2.renderMode != 0) { num2 += 500; } if (num2 > num) { num = num2; canvas = key2; } } if ((Object)(object)canvas == (Object)null) { return false; } if ((string.IsNullOrEmpty(value2) || !(((Object)canvas).name == value2)) && dictionary[canvas].Count < 4) { return false; } Dictionary<TMP_FontAsset, int> dictionary2 = new Dictionary<TMP_FontAsset, int>(); Dictionary<TMP_FontAsset, TMP_Text> dictionary3 = new Dictionary<TMP_FontAsset, TMP_Text>(); foreach (TMP_Text item3 in dictionary[canvas]) { dictionary2[item3.font] = ((!dictionary2.TryGetValue(item3.font, out var value3)) ? 1 : (value3 + 1)); if (!dictionary3.ContainsKey(item3.font)) { dictionary3[item3.font] = item3; } } int num3 = 0; foreach (KeyValuePair<TMP_FontAsset, int> item4 in dictionary2) { if (item4.Value > num3) { font = item4.Key; num3 = item4.Value; } } if ((Object)(object)font == (Object)null) { return false; } material = dictionary3[font].fontSharedMaterial; return true; } } }