using 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 slots = new List(11); public IReadOnlyList Slots => slots; public void Reset() { slots.Clear(); } public void Update(IReadOnlyList snapshots, float elapsedSeconds) { float elapsedSeconds2 = Math.Max(0f, elapsedSeconds); AddNewSlots(snapshots); foreach (EnemySlotState slot in slots) { slot.Apply(snapshots, elapsedSeconds2); } } private void AddNewSlots(IReadOnlyList 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 members = new List(); 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 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 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 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 list = new List(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 ReadSnapshots() { List list = new List(); List 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 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 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 slots = new List(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 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(); 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(); 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(); ((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(); ((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 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 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 list = new List(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 ""; } 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 "" + text + ""; } } 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 <0>__OnSceneLoaded; public static UnityAction <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(); 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(); } } }