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 Monster Respawn Timer v1.0.0
BepInEx/plugins/MonsterRespawnTimer.dll
Decompiled 9 hours agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.Events; 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("MonsterRespawnTimer")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("Monster Respawn Timer")] [assembly: AssemblyTitle("MonsterRespawnTimer")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.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 MonsterRespawnTimer { internal readonly struct EnemySnapshot { public string StableId { get; } public string DisplayName { get; } public string GroupName { get; } public bool IsSpawned { get; } public float RespawnTimer { get; } public EnemySnapshot(string stableId, string displayName, bool isSpawned, float respawnTimer) : this(stableId, displayName, displayName, isSpawned, respawnTimer) { } public EnemySnapshot(string stableId, string displayName, string groupName, bool isSpawned, float respawnTimer) { StableId = stableId; DisplayName = displayName; GroupName = groupName; IsSpawned = isSpawned; RespawnTimer = respawnTimer; } } internal sealed class EnemySlotTracker { public const int MaxSlots = 11; public const float FlashDurationSeconds = 1f; public const float SuddenDecreaseThresholdSeconds = 2f; private readonly List<EnemySlotState> slots = new List<EnemySlotState>(11); public IReadOnlyList<EnemySlotState> Slots => slots; public void Reset() { slots.Clear(); } public void Update(IReadOnlyList<EnemySnapshot> snapshots, float elapsedSeconds) { float elapsedSeconds2 = Math.Max(0f, elapsedSeconds); AddNewSlots(snapshots); foreach (EnemySlotState slot in slots) { slot.Apply(snapshots, elapsedSeconds2); } } private void AddNewSlots(IReadOnlyList<EnemySnapshot> snapshots) { foreach (EnemySnapshot snapshot in snapshots) { if (!string.IsNullOrWhiteSpace(snapshot.StableId)) { string displayKey = EnemySlotState.CreateDisplayKey(snapshot); EnemySlotState enemySlotState = slots.FirstOrDefault((EnemySlotState slot) => slot.DisplayKey == displayKey); if (enemySlotState != null) { enemySlotState.AddMemberIfMissing(snapshot); } else if (slots.Count < 11) { slots.Add(new EnemySlotState(displayKey, snapshot)); } } } } } internal sealed class EnemySlotState { private readonly List<EnemyMemberState> members = new List<EnemyMemberState>(); public string DisplayKey { get; } public string StableId { get; } public string BaseDisplayName { get; private set; } public bool IsAssigned => members.Count > 0; public bool IsVisible { get; private set; } = true; public bool IsSpawned => members.Where((EnemyMemberState member) => member.IsVisible).All((EnemyMemberState member) => member.IsSpawned); public float RespawnTimer => (from member in members where member.IsVisible && !member.IsSpawned select member.RespawnTimer).DefaultIfEmpty(0f).Min(); public string DisplayName { get { if (!IsSwarmKey(DisplayKey) || members.Count <= 1) { return BaseDisplayName; } EnemyMemberState[] array = members.Where((EnemyMemberState member) => member.IsVisible).ToArray(); int num = ((array.Length != 0) ? array.Length : members.Count); int num2 = array.Count((EnemyMemberState member) => member.IsSpawned); return BaseDisplayName + " " + num2.ToString(CultureInfo.InvariantCulture) + "/" + num.ToString(CultureInfo.InvariantCulture); } } public IReadOnlyList<string> TimerTexts => (from member in members where member.IsVisible && !member.IsSpawned select member.TimerText into text where text.Length > 0 select text).ToArray(); public string TimerText => string.Join(" ", TimerTexts); public string FlashText => (from member in members where member.IsVisible select member.FlashText).LastOrDefault((string text) => text.Length > 0) ?? string.Empty; public string StatusText => BuildStatusText(TimerTexts, FlashText); public EnemySlotState(string displayKey, EnemySnapshot snapshot) { DisplayKey = displayKey; StableId = (displayKey.StartsWith("group:", StringComparison.Ordinal) ? displayKey : snapshot.StableId); BaseDisplayName = NormalizeDisplayName(snapshot.DisplayName); AddMemberIfMissing(snapshot); } public static string CreateDisplayKey(EnemySnapshot snapshot) { string text = NormalizeDisplayName(snapshot.GroupName); if (IsSwarmName(text)) { return "group:" + text.ToLowerInvariant(); } return "enemy:" + snapshot.StableId; } public void AddMemberIfMissing(EnemySnapshot snapshot) { if (!members.Any((EnemyMemberState member) => member.StableId == snapshot.StableId)) { members.Add(new EnemyMemberState(snapshot)); } } public void Apply(IReadOnlyList<EnemySnapshot> snapshots, float elapsedSeconds) { foreach (EnemyMemberState member in members) { EnemySnapshot snapshot = snapshots.FirstOrDefault((EnemySnapshot candidate) => candidate.StableId == member.StableId); if (string.IsNullOrEmpty(snapshot.StableId)) { member.MarkUnavailable(elapsedSeconds); } else { member.Apply(snapshot, elapsedSeconds); } } EnemyMemberState[] array = members.Where((EnemyMemberState enemyMemberState) => enemyMemberState.IsVisible).ToArray(); IsVisible = array.Length != 0; string displayName = ((array.Length != 0) ? array[0].DisplayName : members[0].DisplayName); BaseDisplayName = NormalizeDisplayName(displayName); } private static string BuildStatusText(IReadOnlyList<string> timerTexts, string flashText) { if (timerTexts.Count == 0) { return string.Empty; } if (string.IsNullOrEmpty(flashText)) { return string.Join(" ", timerTexts); } if (timerTexts.Count == 1) { return flashText + " " + timerTexts[0]; } List<string> list = new List<string>(timerTexts.Count + 1); for (int i = 0; i < timerTexts.Count - 1; i++) { list.Add(timerTexts[i]); } list.Add(flashText); list.Add(timerTexts[timerTexts.Count - 1]); return string.Join(" ", list); } private static bool IsSwarmKey(string displayKey) { return displayKey.StartsWith("group:", StringComparison.Ordinal); } private static bool IsSwarmName(string displayName) { if (!string.Equals(displayName, "Gnome", StringComparison.OrdinalIgnoreCase)) { return string.Equals(displayName, "Banger", StringComparison.OrdinalIgnoreCase); } return true; } private static string NormalizeDisplayName(string displayName) { if (!string.IsNullOrWhiteSpace(displayName)) { return displayName.Trim(); } return "Enemy"; } } internal sealed class EnemyMemberState { private float lastRespawnTimer; private float flashRemainingSeconds; public string StableId { get; } public string DisplayName { get; private set; } public bool IsVisible { get; private set; } public bool IsSpawned { get; private set; } public float RespawnTimer { get; private set; } public string TimerText { get { if (!IsSpawned) { return FormatSeconds(RespawnTimer); } return string.Empty; } } public string FlashText { get; private set; } public EnemyMemberState(EnemySnapshot snapshot) { StableId = snapshot.StableId; DisplayName = NormalizeDisplayName(snapshot.DisplayName); IsVisible = true; IsSpawned = snapshot.IsSpawned; RespawnTimer = Math.Max(0f, snapshot.RespawnTimer); lastRespawnTimer = RespawnTimer; FlashText = string.Empty; } public void Apply(EnemySnapshot snapshot, float elapsedSeconds) { TickFlash(elapsedSeconds); bool flag = IsVisible && !IsSpawned; float num = lastRespawnTimer; IsVisible = true; DisplayName = NormalizeDisplayName(snapshot.DisplayName); IsSpawned = snapshot.IsSpawned; RespawnTimer = Math.Max(0f, snapshot.RespawnTimer); if (!IsSpawned && flag) { float num2 = num - RespawnTimer; if (num2 - Math.Max(0f, elapsedSeconds) >= 2f) { FlashText = "-" + Math.Max(1, (int)Math.Round(num2, MidpointRounding.AwayFromZero)).ToString(CultureInfo.InvariantCulture) + "s"; flashRemainingSeconds = 1f; } } lastRespawnTimer = RespawnTimer; } public void MarkUnavailable(float elapsedSeconds) { TickFlash(elapsedSeconds); IsVisible = false; } private void TickFlash(float elapsedSeconds) { if (flashRemainingSeconds <= 0f) { FlashText = string.Empty; flashRemainingSeconds = 0f; return; } flashRemainingSeconds -= Math.Max(0f, elapsedSeconds); if (flashRemainingSeconds <= 0f) { FlashText = string.Empty; flashRemainingSeconds = 0f; } } private static string FormatSeconds(float seconds) { return Math.Max(0, (int)Math.Ceiling(seconds)).ToString(CultureInfo.InvariantCulture) + "s"; } private static string NormalizeDisplayName(string displayName) { if (!string.IsNullOrWhiteSpace(displayName)) { return displayName.Trim(); } return "Enemy"; } } internal static class EnemySnapshotReader { private static readonly FieldInfo? SpawnedField = AccessTools.Field(typeof(EnemyParent), "Spawned"); private static bool spawnedReadErrorLogged; private static bool localizedNameErrorLogged; public static List<EnemySnapshot> ReadSnapshots() { List<EnemySnapshot> list = new List<EnemySnapshot>(); List<EnemyParent> list2 = EnemyDirector.instance?.enemiesSpawned; if (list2 == null) { return list; } foreach (EnemyParent item in list2) { if (!((Object)(object)item == (Object)null)) { string text = ReadDisplayName(item); list.Add(new EnemySnapshot(((Object)item).GetInstanceID().ToString(CultureInfo.InvariantCulture), text, ReadGroupName(item, text), ReadSpawned(item), item.DespawnedTimer)); } } return list; } private static bool ReadSpawned(EnemyParent enemyParent) { try { object obj = SpawnedField?.GetValue(enemyParent); if (obj is bool) { return (bool)obj; } } catch (Exception arg) { if (!spawnedReadErrorLogged) { spawnedReadErrorLogged = true; Plugin.Logger.LogError((object)$"MonsterRespawnTimer could not read EnemyParent.Spawned and will use GameObject active state instead: {arg}"); } } return ((Component)enemyParent).gameObject.activeInHierarchy; } private static string ReadDisplayName(EnemyParent enemyParent) { try { LocalizedAsset enemyNameLocalized = enemyParent.enemyNameLocalized; string text = ((enemyNameLocalized != null) ? enemyNameLocalized.GetLocalizedString() : null); if (!string.IsNullOrWhiteSpace(text)) { return text; } } catch (Exception arg) { if (!localizedNameErrorLogged) { localizedNameErrorLogged = true; Plugin.Logger.LogError((object)$"MonsterRespawnTimer could not localize an enemy name and will use the raw enemy name instead: {arg}"); } } if (!string.IsNullOrWhiteSpace(enemyParent.enemyName)) { return enemyParent.enemyName; } return "Enemy"; } private static string ReadGroupName(EnemyParent enemyParent, string fallbackDisplayName) { if (!string.IsNullOrWhiteSpace(enemyParent.enemyName)) { return enemyParent.enemyName; } return fallbackDisplayName; } } internal static class MonsterRespawnTimerController { private const float RefreshIntervalSeconds = 0.25f; private static readonly EnemySlotTracker Tracker = new EnemySlotTracker(); private static MonsterRespawnTimerHudView? hudView; private static float nextRefreshTime; private static float lastRefreshTime; private static bool readinessErrorLogged; public static void Tick(string source) { if (!(Time.unscaledTime < nextRefreshTime)) { float unscaledTime = Time.unscaledTime; float elapsedSeconds = ((lastRefreshTime > 0f) ? (unscaledTime - lastRefreshTime) : 0f); lastRefreshTime = unscaledTime; nextRefreshTime = unscaledTime + 0.25f; TickNow(elapsedSeconds); } } public static void OnRunManagerChangeLevel() { nextRefreshTime = 0f; Tick("RunManager.ChangeLevel"); } public static void OnSceneLoaded(Scene scene, LoadSceneMode mode) { ResetRefresh(); Tick("SceneManager.sceneLoaded"); } public static void OnActiveSceneChanged(Scene oldScene, Scene newScene) { DestroyHud(); Tick("SceneManager.activeSceneChanged"); } public static void OnWillRenderCanvases() { Tick("Canvas.willRenderCanvases"); } public static void DestroyActiveHud() { DestroyHud(); } private static void TickNow(float elapsedSeconds) { if (!IsHudReady()) { MonsterRespawnTimerHudView monsterRespawnTimerHudView = hudView; if (monsterRespawnTimerHudView != null && monsterRespawnTimerHudView.IsAlive) { DestroyHud(); } } else if (!EnsureHud()) { Tracker.Reset(); } else { List<EnemySnapshot> snapshots = EnemySnapshotReader.ReadSnapshots(); Tracker.Update(snapshots, elapsedSeconds); hudView?.Render(Tracker.Slots); } } private static bool EnsureHud() { MonsterRespawnTimerHudView monsterRespawnTimerHudView = hudView; if (monsterRespawnTimerHudView != null && monsterRespawnTimerHudView.IsAlive) { return true; } hudView?.Destroy(); hudView = null; if (!MonsterRespawnTimerHudView.TryCreate(out MonsterRespawnTimerHudView view)) { return false; } hudView = view; Tracker.Reset(); return true; } private static void DestroyHud() { hudView?.Destroy(); hudView = null; Tracker.Reset(); lastRefreshTime = 0f; } private static void ResetRefresh() { nextRefreshTime = 0f; lastRefreshTime = 0f; readinessErrorLogged = false; } private static bool IsHudReady() { try { if ((Object)(object)RunManager.instance == (Object)null) { return false; } if (!SemiFunc.RunIsLevel()) { return false; } if (!MonsterRespawnTimerHudStatus.ShouldRenderForSpectateState(SemiFunc.IsSpectating())) { return false; } } catch (Exception arg) { if (!readinessErrorLogged) { readinessErrorLogged = true; Plugin.Logger.LogError((object)$"MonsterRespawnTimer could not check whether the run is in a level: {arg}"); } return false; } return (Object)(object)GameObject.Find("Game Hud") != (Object)null; } } internal static class MonsterRespawnTimerHudLayout { public const float NameColumnMinWidth = 88f; public const float NameColumnMaxWidth = 154f; public const float StatusColumnWidth = 154f; public const float NameStatusGap = 4f; public const float FlashTimerGapEm = 0.5f; public const float SlotWidth = 312f; public const float SlotHeight = 20f; public const float SlotGap = 1f; public const float RightMargin = 14f; public const float BottomMargin = 6f; public const float NameFontSize = 16f; public const float StatusFontSize = 15.5f; public static float CalculateSlotX(float startX, int index) { return startX; } public static float CalculateSlotY(float bottomY, int index) { return bottomY + (float)index * 21f; } } internal static class MonsterRespawnTimerHudStatus { public const string ProbeText = "EC"; public static bool ShouldRenderForSpectateState(bool isSpectating) { return !isSpectating; } public static bool ShouldShowProbe(IReadOnlyList<EnemySlotState> slotStates) { for (int i = 0; i < slotStates.Count; i++) { if (slotStates[i].IsAssigned && slotStates[i].IsVisible) { return false; } } return true; } } internal sealed class MonsterRespawnTimerHudView { private sealed class SlotView { public GameObject Root { get; } public TextMeshProUGUI NameText { get; } public TextMeshProUGUI StatusText { get; } public SlotView(GameObject root, TextMeshProUGUI nameText, TextMeshProUGUI statusText) { Root = root; NameText = nameText; StatusText = statusText; } public void SetNameColumnWidth(float nameColumnWidth) { //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_002b: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) ((RectTransform)((TMP_Text)NameText).transform).sizeDelta = new Vector2(nameColumnWidth, 20f); ((RectTransform)((TMP_Text)StatusText).transform).anchoredPosition = new Vector2(0f - (nameColumnWidth + 4f), 0f); } } private static readonly Color NameColor = new Color(0.79f, 0.91f, 0.9f, 1f); private static readonly Color TimerColor = new Color(1f, 0.72f, 0.28f, 1f); private static readonly Color FlashColor = new Color(0.45f, 0.9f, 1f, 1f); private readonly List<SlotView> slots = new List<SlotView>(11); private TextMeshProUGUI? probeText; private GameObject? root; public bool IsAlive => (Object)(object)root != (Object)null; public static bool TryCreate(out MonsterRespawnTimerHudView view) { view = new MonsterRespawnTimerHudView(); return view.Create(); } public void Render(IReadOnlyList<EnemySlotState> slotStates) { if ((Object)(object)root == (Object)null) { return; } if ((Object)(object)probeText != (Object)null) { ((TMP_Text)probeText).text = "EC"; ((Component)probeText).gameObject.SetActive(MonsterRespawnTimerHudStatus.ShouldShowProbe(slotStates)); } float nameColumnWidth = CalculateNameColumnWidth(slotStates); for (int i = 0; i < slots.Count; i++) { bool flag = i < slotStates.Count && slotStates[i].IsAssigned && slotStates[i].IsVisible; slots[i].Root.SetActive(flag); if (flag) { EnemySlotState enemySlotState = slotStates[i]; slots[i].SetNameColumnWidth(nameColumnWidth); ((TMP_Text)slots[i].NameText).text = enemySlotState.DisplayName; ((TMP_Text)slots[i].StatusText).text = BuildStatusRichText(enemySlotState); ((Component)slots[i].StatusText).gameObject.SetActive(!string.IsNullOrEmpty(enemySlotState.StatusText)); } } } public void Destroy() { if ((Object)(object)root != (Object)null) { Object.Destroy((Object)(object)root); root = null; } slots.Clear(); probeText = null; } private bool Create() { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Expected O, but got Unknown //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0089: 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_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_009f: 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_00b4: 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_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) Transform val = FindHudParent(); if ((Object)(object)val == (Object)null) { return false; } TMP_FontAsset val2 = FindHudFont(); if ((Object)(object)val2 == (Object)null) { return false; } root = new GameObject("MonsterRespawnTimer HUD", new Type[1] { typeof(RectTransform) }); root.SetActive(true); root.transform.SetParent(val, false); root.layer = ((Component)val).gameObject.layer; RectTransform val3 = (RectTransform)root.transform; val3.anchorMin = Vector2.zero; val3.anchorMax = Vector2.one; val3.pivot = new Vector2(0.5f, 0.5f); val3.offsetMin = Vector2.zero; val3.offsetMax = Vector2.zero; ((Transform)val3).localScale = Vector3.one; for (int i = 0; i < 11; i++) { slots.Add(CreateSlot(i, val2, root.transform)); } probeText = CreateProbe(val2, root.transform); return true; } private static Transform? FindHudParent() { GameObject val = GameObject.Find("Game Hud"); if ((Object)(object)val != (Object)null) { return val.transform; } return null; } private static TMP_FontAsset? FindHudFont() { GameObject val = GameObject.Find("Tax Haul"); if ((Object)(object)val != (Object)null) { TMP_Text component = val.GetComponent<TMP_Text>(); if ((Object)(object)((component != null) ? component.font : null) != (Object)null) { return component.font; } } HealthUI instance = HealthUI.instance; object obj; if (instance == null) { obj = null; } else { TextMeshProUGUI uiText = ((SemiUI)instance).uiText; obj = ((uiText != null) ? ((TMP_Text)uiText).font : null); } if ((Object)obj != (Object)null) { return ((TMP_Text)((SemiUI)HealthUI.instance).uiText).font; } TextMeshProUGUI obj2 = Object.FindObjectOfType<TextMeshProUGUI>(); if (obj2 == null) { return null; } return ((TMP_Text)obj2).font; } private static SlotView CreateSlot(int index, TMP_FontAsset font, Transform parent) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Expected O, but got Unknown //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0072: 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_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: 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_00dd: 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_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject($"MonsterRespawnTimer Slot {index}", new Type[1] { typeof(RectTransform) }); val.transform.SetParent(parent, false); val.layer = ((Component)parent).gameObject.layer; RectTransform val2 = (RectTransform)val.transform; val2.anchorMin = new Vector2(1f, 0f); val2.anchorMax = new Vector2(1f, 0f); val2.pivot = new Vector2(1f, 0f); val2.anchoredPosition = new Vector2(MonsterRespawnTimerHudLayout.CalculateSlotX(-14f, index), MonsterRespawnTimerHudLayout.CalculateSlotY(6f, index)); val2.sizeDelta = new Vector2(312f, 20f); ((Transform)val2).localScale = Vector3.one; TextMeshProUGUI nameText = CreateRightAnchoredText("Name", font, val.transform, Vector2.zero, new Vector2(154f, 20f), NameColor, 16f); TextMeshProUGUI statusText = CreateRightAnchoredText("Status", font, val.transform, new Vector2(-92f, 0f), new Vector2(154f, 20f), TimerColor, 15.5f); val.SetActive(false); return new SlotView(val, nameText, statusText); } private static TextMeshProUGUI CreateProbe(TMP_FontAsset font, Transform parent) { //IL_0018: 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_002a: 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_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_0051: Unknown result type (might be due to invalid IL or missing references) //IL_005b: 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_0070: 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_0085: 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_009a: 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_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("Probe", new Type[1] { typeof(RectTransform) }); val.transform.SetParent(parent, false); val.layer = ((Component)parent).gameObject.layer; RectTransform val2 = (RectTransform)val.transform; val2.anchorMin = new Vector2(1f, 0f); val2.anchorMax = new Vector2(1f, 0f); val2.pivot = new Vector2(1f, 0f); val2.anchoredPosition = new Vector2(-14f, 6f); val2.sizeDelta = new Vector2(42f, 18f); ((Transform)val2).localScale = Vector3.one; TextMeshProUGUI obj = val.AddComponent<TextMeshProUGUI>(); ((TMP_Text)obj).font = font; ((Graphic)obj).color = NameColor; ((TMP_Text)obj).fontSize = 16f; ((TMP_Text)obj).enableAutoSizing = true; ((TMP_Text)obj).fontSizeMin = 8f; ((TMP_Text)obj).fontSizeMax = 16f; ((TMP_Text)obj).enableWordWrapping = false; ((TMP_Text)obj).overflowMode = (TextOverflowModes)1; ((TMP_Text)obj).alignment = (TextAlignmentOptions)516; ((Graphic)obj).raycastTarget = false; ((TMP_Text)obj).text = "EC"; return obj; } private static TextMeshProUGUI CreateRightAnchoredText(string name, TMP_FontAsset font, Transform parent, Vector2 position, Vector2 size, Color color, float fontSize) { //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_0026: 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_003d: 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_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0057: 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_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0077: 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_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0089: 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_00a7: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name, new Type[1] { typeof(RectTransform) }); val.transform.SetParent(parent, false); val.layer = ((Component)parent).gameObject.layer; RectTransform val2 = (RectTransform)val.transform; val2.anchorMin = new Vector2(1f, 0.5f); val2.anchorMax = new Vector2(1f, 0.5f); val2.pivot = new Vector2(1f, 0.5f); val2.anchoredPosition = position; val2.sizeDelta = size; ((Transform)val2).localScale = Vector3.one; TextMeshProUGUI obj = val.AddComponent<TextMeshProUGUI>(); ((TMP_Text)obj).font = font; ((Graphic)obj).color = color; ((TMP_Text)obj).fontSize = fontSize; ((TMP_Text)obj).enableAutoSizing = true; ((TMP_Text)obj).fontSizeMin = 9f; ((TMP_Text)obj).fontSizeMax = fontSize; ((TMP_Text)obj).enableWordWrapping = false; ((TMP_Text)obj).overflowMode = (TextOverflowModes)1; ((TMP_Text)obj).alignment = (TextAlignmentOptions)516; ((Graphic)obj).raycastTarget = false; ((TMP_Text)obj).richText = true; return obj; } private float CalculateNameColumnWidth(IReadOnlyList<EnemySlotState> slotStates) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) float num = 88f; TextMeshProUGUI val = slots.FirstOrDefault()?.NameText; if ((Object)(object)val == (Object)null) { return num; } foreach (EnemySlotState slotState in slotStates) { if (slotState.IsAssigned && slotState.IsVisible) { float num2 = ((TMP_Text)val).GetPreferredValues(slotState.DisplayName).x + 2f; num = Mathf.Max(num, Mathf.Min(154f, num2)); } } return num; } private static string BuildStatusRichText(EnemySlotState state) { IReadOnlyList<string> timerTexts = state.TimerTexts; string flashText = state.FlashText; if (timerTexts.Count == 0) { return string.Empty; } if (string.IsNullOrEmpty(flashText)) { return string.Join(" ", timerTexts.Select(FormatTimer)); } if (timerTexts.Count == 1) { return FormatFlash(flashText) + FormatFlashTimerGap() + FormatTimer(timerTexts[0]); } List<string> list = new List<string>(timerTexts.Count + 1); for (int i = 0; i < timerTexts.Count - 1; i++) { list.Add(FormatTimer(timerTexts[i])); } list.Add(FormatFlash(flashText) + FormatFlashTimerGap() + FormatTimer(timerTexts[timerTexts.Count - 1])); return string.Join(" ", list); } private static string FormatFlashTimerGap() { return "<space=" + 0.5f.ToString("0.###", CultureInfo.InvariantCulture) + "em>"; } private static string FormatTimer(string text) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return Colorize(text, TimerColor); } private static string FormatFlash(string text) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return Colorize(text, FlashColor); } private static string Colorize(string text, Color color) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) return "<color=#" + ColorUtility.ToHtmlStringRGB(color) + ">" + text + "</color>"; } } internal sealed class MonsterRespawnTimerRunner : MonoBehaviour { private void Awake() { Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject); } private void Update() { MonsterRespawnTimerController.Tick("Runner.Update"); } } [BepInPlugin("fangjx114514.monsterrespawntimer", "Monster Respawn Timer", "1.0.0")] public sealed class Plugin : BaseUnityPlugin { [CompilerGenerated] private static class <>O { public static UnityAction<Scene, LoadSceneMode> <0>__OnSceneLoaded; public static UnityAction<Scene, Scene> <1>__OnActiveSceneChanged; public static WillRenderCanvases <2>__OnWillRenderCanvases; } private Harmony? harmony; private GameObject? runnerObject; internal static ManualLogSource Logger { get; private set; } private void Awake() { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Expected O, but got Unknown //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Expected O, but got Unknown Logger = ((BaseUnityPlugin)this).Logger; ((Component)this).gameObject.transform.parent = null; ((Object)((Component)this).gameObject).hideFlags = (HideFlags)61; harmony = new Harmony("fangjx114514.monsterrespawntimer"); try { harmony.PatchAll(); } catch (Exception arg) { harmony.UnpatchSelf(); harmony = null; Logger.LogError((object)$"MonsterRespawnTimer failed to patch game methods and will stay disabled: {arg}"); ((Behaviour)this).enabled = false; return; } runnerObject = new GameObject("MonsterRespawnTimer Runner"); Object.DontDestroyOnLoad((Object)(object)runnerObject); ((Object)runnerObject).hideFlags = (HideFlags)61; runnerObject.AddComponent<MonsterRespawnTimerRunner>(); SceneManager.sceneLoaded += MonsterRespawnTimerController.OnSceneLoaded; SceneManager.activeSceneChanged += MonsterRespawnTimerController.OnActiveSceneChanged; object obj = <>O.<2>__OnWillRenderCanvases; if (obj == null) { WillRenderCanvases val = MonsterRespawnTimerController.OnWillRenderCanvases; <>O.<2>__OnWillRenderCanvases = val; obj = (object)val; } Canvas.willRenderCanvases += (WillRenderCanvases)obj; Logger.LogInfo((object)"Plugin fangjx114514.monsterrespawntimer build 1.0.0 is loaded."); } private void Update() { MonsterRespawnTimerController.Tick("Plugin.Update"); } private void LateUpdate() { MonsterRespawnTimerController.Tick("Plugin.LateUpdate"); } private void FixedUpdate() { MonsterRespawnTimerController.Tick("Plugin.FixedUpdate"); } private void OnDestroy() { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Expected O, but got Unknown SceneManager.sceneLoaded -= MonsterRespawnTimerController.OnSceneLoaded; SceneManager.activeSceneChanged -= MonsterRespawnTimerController.OnActiveSceneChanged; object obj = <>O.<2>__OnWillRenderCanvases; if (obj == null) { WillRenderCanvases val = MonsterRespawnTimerController.OnWillRenderCanvases; <>O.<2>__OnWillRenderCanvases = val; obj = (object)val; } Canvas.willRenderCanvases -= (WillRenderCanvases)obj; MonsterRespawnTimerController.DestroyActiveHud(); if ((Object)(object)runnerObject != (Object)null) { Object.Destroy((Object)(object)runnerObject); runnerObject = null; } Harmony? obj2 = harmony; if (obj2 != null) { obj2.UnpatchSelf(); } harmony = null; } } internal static class PluginMetadata { public const string Guid = "fangjx114514.monsterrespawntimer"; public const string Name = "Monster Respawn Timer"; public const string Version = "1.0.0"; } [HarmonyPatch] internal static class MonsterRespawnTimerHarmonyPatches { [HarmonyPatch(typeof(SemiFunc), "OnSceneSwitch")] [HarmonyPrefix] private static void Prefix() { MonsterRespawnTimerController.DestroyActiveHud(); } [HarmonyPatch(typeof(RoundDirector), "Update")] [HarmonyPostfix] private static void Postfix() { MonsterRespawnTimerController.Tick("RoundDirector.Update"); } [HarmonyPatch(typeof(RunManager), "Update")] [HarmonyPostfix] private static void RunManagerUpdatePostfix() { MonsterRespawnTimerController.Tick("RunManager.Update"); } [HarmonyPatch(typeof(EnemyDirector), "Update")] [HarmonyPostfix] private static void EnemyDirectorUpdatePostfix() { MonsterRespawnTimerController.Tick("EnemyDirector.Update"); } [HarmonyPatch(typeof(RunManager), "ChangeLevel")] [HarmonyPostfix] private static void RunManagerChangeLevelPostfix() { MonsterRespawnTimerController.OnRunManagerChangeLevel(); } } }