using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Globalization; using System.Reflection; using System.Reflection.Emit; 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 EntityStates; using EntityStates.FalseSon; using EntityStates.ScavBackpack; using HarmonyLib; using LateGamePerformance.Patches; using LateGamePerformance.Ui; using Microsoft.CodeAnalysis; using Rewired; using RoR2; using RoR2.Navigation; using RoR2.Skills; using RoR2.UI; using RoR2.UI.SkinControllers; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.Networking; using UnityEngine.Rendering; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("RoR2")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("LateGamePerformance")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.5.0.0")] [assembly: AssemblyInformationalVersion("1.5.0")] [assembly: AssemblyProduct("LateGamePerformance")] [assembly: AssemblyTitle("LateGamePerformance")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.5.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 LateGamePerformance { internal sealed class DpsHud : MonoBehaviour { private sealed class RowView { internal RectTransform root; internal Image bar; internal HGTextMeshProUGUI name; internal HGTextMeshProUGUI damage; internal HGTextMeshProUGUI share; internal HGTextMeshProUGUI dps; internal HGTextMeshProUGUI now; } private const float Width = 470f; private const float Pad = 14f; private const float TitleHeight = 30f; private const float ColumnHeight = 20f; private const float RowHeight = 30f; private const float FooterHeight = 22f; private const float NameWidth = 150f; private const float DamageWidth = 78f; private const float ShareWidth = 56f; private const float DpsWidth = 66f; private const float NowWidth = 72f; private static readonly Color[] Ranks = (Color[])(object)new Color[3] { new Color(1f, 0.82f, 0.36f, 1f), new Color(0.8f, 0.84f, 0.9f, 1f), new Color(0.79f, 0.55f, 0.36f, 1f) }; private Canvas canvas; private RectTransform panel; private RectTransform rowRoot; private HGTextMeshProUGUI clock; private HGTextMeshProUGUI footer; private readonly List views = new List(); private bool hidden; internal static DpsHud Instance { get; private set; } internal static void Install(GameObject host) { Instance = host.AddComponent(); } private void Update() { //IL_0013: 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) if (DpsMeterPatch.Active && DpsMeterPatch.ToggleKey != null) { KeyboardShortcut value = DpsMeterPatch.ToggleKey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { hidden = !hidden; } } if (!DpsMeterPatch.Active || hidden || !DpsMeterPatch.ShowDuringRun.Value || !Object.op_Implicit((Object)(object)Run.instance) || !Object.op_Implicit((Object)(object)Stage.instance) || ModMenu.IsOpen) { UiFactory.SetVisible(canvas, visible: false); return; } GameSkin.Refresh(); if (GameSkin.Ready) { if ((Object)(object)canvas == (Object)null) { Build(); } UiFactory.SetVisible(canvas, visible: true); Refresh(); } } private void OnDestroy() { if ((Object)(object)canvas != (Object)null) { Object.Destroy((Object)(object)((Component)canvas).gameObject); } } private void Build() { //IL_0054: 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_0078: 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_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_01ac: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: Expected O, but got Unknown //IL_021d: Unknown result type (might be due to invalid IL or missing references) canvas = UiFactory.CreateCanvas("LGP_DamageMeter", 100, interactive: false); panel = UiFactory.Panel(((Component)canvas).transform, "Panel"); float value = DpsMeterPatch.PanelX.Value; float num = 1f - DpsMeterPatch.PanelY.Value; panel.anchorMin = new Vector2(value, num); panel.anchorMax = new Vector2(value, num); panel.pivot = new Vector2(value, num); panel.anchoredPosition = Vector2.zero; panel.sizeDelta = new Vector2(470f, 200f); UiFactory.Row(((TMP_Text)UiFactory.Label((Transform)(object)panel, "Title", "DAMAGE THIS STAGE", GameSkin.TextKind.Header, 22f, (TextAlignmentOptions)513)).rectTransform, 14f, 442f, 14f, 30f); clock = UiFactory.Label((Transform)(object)panel, "Clock", "0:00", GameSkin.TextKind.Header, 22f, (TextAlignmentOptions)516); UiFactory.Row(((TMP_Text)clock).rectTransform, 14f, 442f, 14f, 30f); float num2 = 44f; Column("Player", (TextAlignmentOptions)513, 0, num2); Column("Damage", (TextAlignmentOptions)516, 1, num2); Column("Share", (TextAlignmentOptions)516, 2, num2); Column("DPS", (TextAlignmentOptions)516, 3, num2); Column("Now 6s", (TextAlignmentOptions)516, 4, num2); GameObject val = new GameObject("Rows", new Type[1] { typeof(RectTransform) }); rowRoot = val.GetComponent(); ((Transform)rowRoot).SetParent((Transform)(object)panel, false); UiFactory.Row(rowRoot, 0f, 470f, num2 + 20f, 0f); footer = UiFactory.Label((Transform)(object)panel, "Footer", string.Empty, GameSkin.TextKind.Body, 15f, (TextAlignmentOptions)513); ((Graphic)footer).color = GameSkin.QuietColor; } private void Column(string text, TextAlignmentOptions alignment, int index, float top) { //IL_0018: 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) HGTextMeshProUGUI obj = UiFactory.Label((Transform)(object)panel, "Column" + text, text, GameSkin.TextKind.Body, 16f, alignment); ((Graphic)obj).color = GameSkin.QuietColor; UiFactory.Row(((TMP_Text)obj).rectTransform, 14f + OffsetOf(index), WidthOf(index), top, 20f); } private static float WidthOf(int index) { return index switch { 0 => 150f, 1 => 78f, 2 => 56f, 3 => 66f, _ => 72f, }; } private static float OffsetOf(int index) { float num = 0f; for (int i = 0; i < index; i++) { num += WidthOf(i); } return num; } private RowView CreateRow(int index) { //IL_0024: 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) RectTransform component = new GameObject("Row" + index, new Type[1] { typeof(RectTransform) }).GetComponent(); ((Transform)component).SetParent((Transform)(object)rowRoot, false); UiFactory.Row(component, 0f, 470f, (float)index * 30f, 30f); RowView obj = new RowView { root = component, bar = UiFactory.Fill((Transform)(object)component, "Bar", new Color(1f, 1f, 1f, 0.16f)) }; UiFactory.Row(((Graphic)obj.bar).rectTransform, 7f, 456f, 2f, 26f); obj.name = Cell(component, "Name", 0, (TextAlignmentOptions)513, 20f, GameSkin.TextKind.Body); obj.damage = Cell(component, "Damage", 1, (TextAlignmentOptions)516, 20f, GameSkin.TextKind.Header); obj.share = Cell(component, "Share", 2, (TextAlignmentOptions)516, 19f, GameSkin.TextKind.Body); obj.dps = Cell(component, "Dps", 3, (TextAlignmentOptions)516, 19f, GameSkin.TextKind.Body); obj.now = Cell(component, "Now", 4, (TextAlignmentOptions)516, 20f, GameSkin.TextKind.Header); return obj; } private static HGTextMeshProUGUI Cell(RectTransform parent, string name, int index, TextAlignmentOptions alignment, float size, GameSkin.TextKind kind) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) HGTextMeshProUGUI obj = UiFactory.Label((Transform)(object)parent, name, string.Empty, kind, size, alignment); UiFactory.Row(((TMP_Text)obj).rectTransform, 14f + OffsetOf(index), WidthOf(index), 0f, 30f); return obj; } private void Refresh() { //IL_029a: Unknown result type (might be due to invalid IL or missing references) //IL_029f: Unknown result type (might be due to invalid IL or missing references) //IL_02a3: Unknown result type (might be due to invalid IL or missing references) //IL_02e9: 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_0129: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_021b: Unknown result type (might be due to invalid IL or missing references) //IL_0222: Unknown result type (might be due to invalid IL or missing references) //IL_0229: Unknown result type (might be due to invalid IL or missing references) //IL_0235: Unknown result type (might be due to invalid IL or missing references) //IL_0256: Unknown result type (might be due to invalid IL or missing references) List list = DpsMeterPatch.Snapshot(); while (views.Count < list.Count) { views.Add(CreateRow(views.Count)); } for (int i = list.Count; i < views.Count; i++) { ((Component)views[i].root).gameObject.SetActive(false); } float stageSeconds = DpsMeterPatch.StageSeconds; double num = 0.0; for (int j = 0; j < list.Count; j++) { num += list[j].total; } double num2 = ((list.Count > 0) ? list[0].total : 0.0); for (int k = 0; k < list.Count; k++) { DpsMeterPatch.Row row = list[k]; RowView rowView = views[k]; ((Component)rowView.root).gameObject.SetActive(true); Color val = ((k < Ranks.Length && row.total > 0.0) ? Ranks[k] : GameSkin.BodyColor); ((Graphic)rowView.name).color = val; ((TMP_Text)rowView.name).text = row.name; ((TMP_Text)rowView.damage).text = Short(row.total); ((TMP_Text)rowView.share).text = ((num > 0.0) ? (((float)(row.total / num) * 100f).ToString("0", CultureInfo.InvariantCulture) + "%") : "-"); ((TMP_Text)rowView.dps).text = Short((stageSeconds > 0.5f) ? (row.total / (double)stageSeconds) : 0.0); ((TMP_Text)rowView.now).text = Short(row.Recent); float num3 = ((num2 > 0.0) ? Mathf.Clamp01((float)(row.total / num2)) : 0f); ((Graphic)rowView.bar).color = new Color(val.r, val.g, val.b, 0.2f); ((Graphic)rowView.bar).rectTransform.sizeDelta = new Vector2(456f * num3, 26f); } ((TMP_Text)clock).text = Clock(stageSeconds); HGTextMeshProUGUI obj = footer; string arg = Short(num); KeyboardShortcut value = DpsMeterPatch.ToggleKey.Value; ((TMP_Text)obj).text = $"Total {arg} - {((KeyboardShortcut)(ref value)).MainKey} hides this"; float num4 = 64f; float num5 = num4 + (float)list.Count * 30f + 22f + 14f; panel.sizeDelta = new Vector2(470f, num5); UiFactory.Row(((TMP_Text)footer).rectTransform, 14f, 442f, num4 + (float)list.Count * 30f + 2f, 22f); } private static string Clock(float seconds) { int num = Mathf.Max(0, Mathf.FloorToInt(seconds)); return (num / 60).ToString(CultureInfo.InvariantCulture) + ":" + (num % 60).ToString("00", CultureInfo.InvariantCulture); } private static string Short(double value) { if (value >= 1000000.0) { return (value / 1000000.0).ToString("0.0", CultureInfo.InvariantCulture) + "M"; } if (value >= 1000.0) { return (value / 1000.0).ToString("0.0", CultureInfo.InvariantCulture) + "k"; } return value.ToString("0", CultureInfo.InvariantCulture); } } internal static class MenuCatalog { private sealed class Entry { internal string Category; internal string Title; internal string Blurb; } internal const string Gameplay = "Gameplay"; internal const string Maintenance = "Fixes and performance"; private static readonly Dictionary features = new Dictionary { ["SkipIntro"] = new Entry { Category = "Gameplay", Title = "Skip the intro video", Blurb = "Cuts the engine logo and the ship cutscene, so the game goes almost straight to the title screen." }, ["CritOverstack"] = new Entry { Category = "Gameplay", Title = "Crit overstacking", Blurb = "Crit chance above 100 percent turns into extra crit damage instead of counting for nothing." }, ["AutoSprint"] = new Entry { Category = "Gameplay", Title = "Automatic sprint", Blurb = "Your character sprints while it moves. Hold the sprint key to walk instead." }, ["EmptyChests"] = new Entry { Category = "Gameplay", Title = "Clear empty chests", Blurb = "Opened chests, used barrels, and spent shops disappear." }, ["Sharing"] = new Entry { Category = "Gameplay", Title = "Share items and money", Blurb = "An item one player picks up goes to everybody, and every player gets the full gold. Only the host needs this mod. Off by default, because it makes a group much stronger than the base game intends." }, ["DpsMeter"] = new Entry { Category = "Gameplay", Title = "Damage meter", Blurb = "A table on screen shows the damage each player did on the stage you are on. It also gives the share of the total and damage per second." }, ["StatPoints"] = new Entry { Category = "Gameplay", Title = "Character stat points", Blurb = "Earn a point every few character levels and spend it on health, damage, attack speed, crit, armor, regeneration, or speed. Press F3 during a run, or click the badge, to open the point window. Only the host can spend. A player who joins sees the window, but the buttons stay off there. The stats would never reach the machine that runs the game." }, ["BugFixes"] = new Entry { Category = "Fixes and performance", Title = "Fix game bugs", Blurb = "Stops errors that the base game throws during play." }, ["BuffStatsFilter"] = new Entry { Category = "Fixes and performance", Title = "Faster damage-over-time ticks", Blurb = "Bleed and poison stacks no longer recalculate every stat on every tick." }, ["NavPathBudget"] = new Entry { Category = "Fixes and performance", Title = "Spread out enemy pathfinding", Blurb = "Spreads the path work of an enemy wave over several frames." }, ["TargetSearchGc"] = new Entry { Category = "Fixes and performance", Title = "Cheaper target search", Blurb = "Missiles, drones, and turrets no longer create garbage when they look for a target." }, ["DamageNumberBatch"] = new Entry { Category = "Fixes and performance", Title = "Faster damage numbers", Blurb = "Big hits no longer slow the game down with damage numbers." }, ["RaycastThrottle"] = new Entry { Category = "Fixes and performance", Title = "Cheaper enemy steering", Blurb = "Enemies check for obstacles less often." }, ["HealthBarThrottle"] = new Entry { Category = "Fixes and performance", Title = "Cheaper health bars", Blurb = "Health bars refresh on a timer instead of every frame." }, ["TeamItemCountCache"] = new Entry { Category = "Fixes and performance", Title = "Faster teleporter events", Blurb = "Stops repeated scans of every character during a teleporter fight." }, ["ModelCameraPass"] = new Entry { Category = "Fixes and performance", Title = "Cheaper character drawing", Blurb = "The camera examines every character once instead of twice, and skips the work for characters that draw nothing." }, ["PerfReadout"] = new Entry { Category = "Fixes and performance", Title = "Performance readout", Blurb = "Frame times and entity counts in the log. The panel starts closed. Press F6 to show it or hide it." }, ["FixedTimeCatchup"] = new Entry { Category = "Fixes and performance", Title = "Stop freeze spirals", Blurb = "Under heavy load the game slows down for a moment instead of freezing." } }; private static readonly HashSet playerFacing = new HashSet { "Menu/ToggleKey", "SkipIntro/AlsoSkipLogoScreen", "Sharing/ShareItems", "Sharing/ShareMoney", "Sharing/ShareWhiteItems", "Sharing/ShareGreenItems", "Sharing/ShareRedItems", "Sharing/ShareBossItems", "Sharing/ShareLunarItems", "Sharing/ShareVoidItems", "Sharing/ShareToDeadPlayers", "Sharing/ShareEquipment", "Sharing/DropReplacedEquipment", "Sharing/SharePrinterOutput", "Sharing/ScaleSharedLoot", "Sharing/MoneyShareScalar", "EmptyChests/Chests", "EmptyChests/Barrels", "EmptyChests/Multishops", "EmptyChests/ScavengerBags", "EmptyChests/DelaySeconds", "AutoSprint/HoldSprintKeyToWalk", "AutoSprint/HoldOffSeconds", "DpsMeter/ShowDuringRun", "DpsMeter/ToggleKey", "DpsMeter/IncludeMinions", "DpsMeter/CountAllDamage", "DpsMeter/PanelX", "DpsMeter/PanelY", "StatPoints/ShowBadge", "StatPoints/OpenKey", "StatPoints/BadgeX", "StatPoints/BadgeY", "StatPoints/CharacterLevelsPerPoint", "StatPoints/MaxPointsPerStat", "StatPoints/HealthPerPoint", "StatPoints/RegenerationPerPoint", "StatPoints/DamagePerPoint", "StatPoints/AttackSpeedPerPoint", "StatPoints/CritChancePerPoint", "StatPoints/ArmorPerPoint", "StatPoints/MoveSpeedPerPoint", "FixedTimeCatchup/MaxCatchUpTicks", "ModelCameraPass/OverlayFrameStride", "PerfReadout/ShowPanel", "PerfReadout/ToggleKey", "PerfReadout/LogSeconds" }; internal static bool IsPlayerFacing(string section, string key) { return playerFacing.Contains(section + "/" + key); } internal static string CategoryOf(string featureKey) { if (!features.TryGetValue(featureKey, out var value)) { return "Gameplay"; } return value.Category; } internal static string TitleOf(string featureKey) { if (!features.TryGetValue(featureKey, out var value)) { return featureKey; } return value.Title; } internal static string BlurbOf(string featureKey, string fallback) { if (!features.TryGetValue(featureKey, out var value)) { return fallback; } return value.Blurb; } } internal sealed class ModMenu : MonoBehaviour { private const float WindowWidth = 1000f; private const float WindowHeight = 620f; private const float ListWidth = 340f; private static ConfigEntry showEverySetting; private Canvas canvas; private RectTransform listContent; private RectTransform detailContent; private HGTextMeshProUGUI detailTitle; private HGTextMeshProUGUI detailBlurb; private HGTextMeshProUGUI detailState; private Button showEveryButton; private readonly List rows = new List(); private readonly List listLabels = new List(); private readonly List listPicks = new List(); private readonly List listGroups = new List(); private string selectedKey; private bool listDirty = true; private bool detailDirty = true; private readonly CursorHold cursor = new CursorHold(); private ConfigEntryBase awaitingBind; private SettingRow awaitingRow; internal static ModMenu Instance { get; private set; } internal static bool IsOpen { get; private set; } internal static ConfigEntry Hotkey { get; private set; } internal static void Install(Harmony harmony, GameObject host) { //IL_0019: 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_0081: Expected O, but got Unknown Hotkey = Plugin.Cfg.Bind("Menu", "ToggleKey", new KeyboardShortcut((KeyCode)285, Array.Empty()), "Key that opens and closes the settings page."); showEverySetting = Plugin.Cfg.Bind("Menu", "ShowEverySetting", false, "Also show the fine-tuning values that most players never need."); harmony.Patch((MethodBase)AccessTools.Method(typeof(PlayerCharacterMasterController), "CanSendBodyInput", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(ModMenu), "BlockInputPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Instance = host.AddComponent(); } private static void BlockInputPostfix(ref bool __result, ref bool onlyAllowMovement) { if (IsOpen || StatPointsWindow.IsOpen) { __result = false; onlyAllowMovement = false; } } internal static void Close() { Instance?.SetOpen(open: false); } internal static void OpenAt(string featureKey) { if (!((Object)(object)Instance == (Object)null)) { Instance.selectedKey = featureKey; Instance.detailDirty = true; Instance.SetOpen(open: true); } } private void Update() { //IL_001c: 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) if (awaitingBind != null) { CaptureKey(); } else if (Hotkey != null) { KeyboardShortcut value = Hotkey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { SetOpen(!IsOpen); } } if (!IsOpen) { return; } if (Input.GetKeyDown((KeyCode)27)) { SetOpen(open: false); return; } cursor.Set(wanted: true); GameSkin.Refresh(); if (GameSkin.Ready) { if ((Object)(object)canvas == (Object)null) { Build(); } if (listDirty) { BuildList(); } if (detailDirty) { BuildDetail(); } RefreshListState(); } } private void OnDisable() { SetOpen(open: false); } private void OnDestroy() { if ((Object)(object)canvas != (Object)null) { Object.Destroy((Object)(object)((Component)canvas).gameObject); } } private void SetOpen(bool open) { if (open != IsOpen) { IsOpen = open; awaitingBind = null; awaitingRow = null; UiFactory.SetVisible(canvas, open); if (open) { detailDirty = true; } else { cursor.Release(); } } } private void CaptureKey() { //IL_0025: 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_002b: 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_0036: Invalid comparison between Unknown and I4 //IL_003e: 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) if (!Input.anyKeyDown) { return; } foreach (KeyCode value in Enum.GetValues(typeof(KeyCode))) { if (Input.GetKeyDown(value)) { if ((int)value != 27) { awaitingBind.BoxedValue = (object)new KeyboardShortcut(value, Array.Empty()); } awaitingRow?.Refresh(); awaitingRow?.SetBindPrompt(waiting: false); awaitingBind = null; awaitingRow = null; break; } } } private void Build() { //IL_003a: 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_0096: 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_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_02c8: Unknown result type (might be due to invalid IL or missing references) canvas = UiFactory.CreateCanvas("LGP_Settings", 200, interactive: true); Image obj = UiFactory.Fill(((Component)canvas).transform, "Dim", new Color(0f, 0f, 0f, 0.66f)); UiFactory.Stretch(((Graphic)obj).rectTransform); ((Graphic)obj).raycastTarget = true; RectTransform obj2 = UiFactory.Panel(((Component)canvas).transform, "Window", GameSkin.PanelKind.Main, opaque: true); obj2.anchorMin = new Vector2(0.5f, 0.5f); obj2.anchorMax = new Vector2(0.5f, 0.5f); obj2.pivot = new Vector2(0.5f, 0.5f); obj2.anchoredPosition = Vector2.zero; obj2.sizeDelta = new Vector2(1000f, 620f); RectTransform obj3 = UiFactory.Vertical((Transform)(object)obj2, "Body", 10f, 18); UiFactory.Stretch(obj3); RectTransform parent = UiFactory.Horizontal((Transform)(object)obj3, "TitleBar", 10f, 34f); UiFactory.Size((Component)(object)UiFactory.Label((Transform)(object)parent, "Title", "LateGamePerformance 1.5.0", GameSkin.TextKind.Header, 26f, (TextAlignmentOptions)513), 800f, 32f); UiFactory.Size((Component)(object)UiFactory.Button((Transform)(object)parent, "Close", "Close", delegate { SetOpen(open: false); }), 120f, 32f); RectTransform obj4 = UiFactory.Horizontal((Transform)(object)obj3, "Columns", 16f, 0f); UiFactory.Size((Component)(object)obj4, 0f, 530f); RectTransform val = UiFactory.Vertical((Transform)(object)obj4, "Left", 8f); UiFactory.Size((Component)(object)val, 340f, 530f); listContent = UiFactory.Scroll((Transform)(object)val, "FeatureList", out var scroll); UiFactory.Size((Component)(object)scroll, 340f, 480f); showEveryButton = UiFactory.Button((Transform)(object)val, "ShowEvery", string.Empty, delegate { showEverySetting.Value = !showEverySetting.Value; listDirty = true; detailDirty = true; }); UiFactory.Size((Component)(object)showEveryButton, 340f, 34f); RectTransform val2 = UiFactory.Vertical((Transform)(object)obj4, "Right", 6f); UiFactory.Size((Component)(object)val2, 608f, 530f); detailTitle = UiFactory.Label((Transform)(object)val2, "DetailTitle", string.Empty, GameSkin.TextKind.Header, 24f, (TextAlignmentOptions)513); UiFactory.Size((Component)(object)detailTitle, 0f, 30f); detailBlurb = UiFactory.Label((Transform)(object)val2, "DetailBlurb", string.Empty, GameSkin.TextKind.Body, 17f, (TextAlignmentOptions)257); ((TMP_Text)detailBlurb).enableWordWrapping = true; UiFactory.Size((Component)(object)detailBlurb, 0f, 64f); detailState = UiFactory.Label((Transform)(object)val2, "DetailState", string.Empty, GameSkin.TextKind.Body, 16f, (TextAlignmentOptions)513); ((Graphic)detailState).color = GameSkin.QuietColor; UiFactory.Size((Component)(object)detailState, 0f, 22f); detailContent = UiFactory.Scroll((Transform)(object)val2, "Detail", out var scroll2); UiFactory.Size((Component)(object)scroll2, 608f, 400f); listDirty = true; detailDirty = true; } private void BuildList() { listDirty = false; Clear(listContent); listLabels.Clear(); listPicks.Clear(); listGroups.Clear(); AddCategory("Gameplay"); if (showEverySetting.Value) { AddCategory("Fixes and performance"); } HGTextMeshProUGUI componentInChildren = ((Component)showEveryButton).GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null) { ((TMP_Text)componentInChildren).text = (showEverySetting.Value ? "Hide fixes and tuning" : "Show fixes and tuning"); } } private void AddCategory(string category) { UiFactory.Size((Component)(object)UiFactory.Label((Transform)(object)listContent, "Header" + category, category.ToUpperInvariant(), GameSkin.TextKind.Header, 17f, (TextAlignmentOptions)513), 0f, 26f); IReadOnlyList groups = PatchRegistry.Groups; for (int i = 0; i < groups.Count; i++) { PatchRegistry.Group obj = groups[i]; if (!(MenuCatalog.CategoryOf(obj.Key) != category)) { if (string.IsNullOrEmpty(selectedKey)) { selectedKey = obj.Key; } RectTransform parent = UiFactory.Horizontal((Transform)(object)listContent, "Row" + obj.Key, 8f, 34f); PatchRegistry.Group captured = obj; Button val = UiFactory.Button((Transform)(object)parent, "Toggle", string.Empty, delegate { PatchRegistry.SetEnabled(captured, !captured.Active); }); UiFactory.Size((Component)(object)val, 62f, 30f); Button obj2 = UiFactory.Button((Transform)(object)parent, "Pick", MenuCatalog.TitleOf(obj.Key), delegate { selectedKey = captured.Key; detailDirty = true; }); UiFactory.Size((Component)(object)obj2, 254f, 30f); HGTextMeshProUGUI componentInChildren = ((Component)obj2).GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null) { ((TMP_Text)componentInChildren).alignment = (TextAlignmentOptions)513; ((TMP_Text)componentInChildren).fontSize = 17f; } listLabels.Add(((Component)val).GetComponentInChildren()); listPicks.Add(componentInChildren); listGroups.Add(obj); } } } private void RefreshListState() { //IL_00ac: 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_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < listGroups.Count && i < listLabels.Count; i++) { HGTextMeshProUGUI val = listLabels[i]; if ((Object)(object)val != (Object)null) { bool active = listGroups[i].Active; ((TMP_Text)val).text = (active ? "ON" : "OFF"); ((Graphic)val).color = (active ? GameSkin.HeaderColor : GameSkin.QuietColor); } if (i < listPicks.Count && (Object)(object)listPicks[i] != (Object)null) { ((Graphic)listPicks[i]).color = ((listGroups[i].Key == selectedKey) ? GameSkin.HeaderColor : GameSkin.BodyColor); } } } private void BuildDetail() { //IL_018c: 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) detailDirty = false; Clear(detailContent); rows.Clear(); PatchRegistry.Group obj = FindSelected(); if (obj == null) { return; } ((TMP_Text)detailTitle).text = MenuCatalog.TitleOf(obj.Key); ((TMP_Text)detailBlurb).text = MenuCatalog.BlurbOf(obj.Key, obj.Description); ((TMP_Text)detailState).text = (obj.Active ? "Turned on" : "Turned off"); if (!string.IsNullOrEmpty(obj.Error)) { HGTextMeshProUGUI obj2 = detailState; ((TMP_Text)obj2).text = ((TMP_Text)obj2).text + " - last error: " + obj.Error; } int num = 0; ConfigEntryBase[] configEntries = Plugin.Cfg.GetConfigEntries(); foreach (ConfigEntryBase val in configEntries) { if (!(val.Definition.Section != obj.Key) && (showEverySetting.Value || MenuCatalog.IsPlayerFacing(obj.Key, val.Definition.Key))) { rows.Add(SettingRow.Create((Transform)(object)detailContent, val, RequestBind)); num++; } } if (num == 0) { HGTextMeshProUGUI obj3 = UiFactory.Label((Transform)(object)detailContent, "None", "Nothing to set for this one.", GameSkin.TextKind.Body, 17f, (TextAlignmentOptions)513); ((Graphic)obj3).color = GameSkin.QuietColor; UiFactory.Size((Component)(object)obj3, 0f, 26f); } HGTextMeshProUGUI obj4 = UiFactory.Label((Transform)(object)detailContent, "Note", "Changes save by themselves.", GameSkin.TextKind.Body, 15f, (TextAlignmentOptions)513); ((Graphic)obj4).color = GameSkin.QuietColor; UiFactory.Size((Component)(object)obj4, 0f, 24f); } private void RequestBind(ConfigEntryBase entry) { awaitingBind = entry; awaitingRow = null; for (int i = 0; i < rows.Count; i++) { bool flag = rows[i].Entry == entry; rows[i].SetBindPrompt(flag); if (flag) { awaitingRow = rows[i]; } } } private PatchRegistry.Group FindSelected() { IReadOnlyList groups = PatchRegistry.Groups; for (int i = 0; i < groups.Count; i++) { if (groups[i].Key == selectedKey) { return groups[i]; } } if (groups.Count <= 0) { return null; } return groups[0]; } private static void Clear(RectTransform parent) { if (!((Object)(object)parent == (Object)null)) { for (int num = ((Transform)parent).childCount - 1; num >= 0; num--) { Object.Destroy((Object)(object)((Component)((Transform)parent).GetChild(num)).gameObject); } } } } internal static class PatchGroups { internal static void RegisterAll() { PatchRegistry.Register("BuffStatsFilter", "Skip full stat recalculation when a pure damage-over-time buff changes count. Bleed and poison stacks no longer force a stat pass per tick.", defaultOn: true, BuffStatsFilterPatch.Apply); PatchRegistry.Register("NavPathBudget", "Limit synchronous AI path computations per physics tick and stagger repath timers. The game ships this throttle in a broken state.", defaultOn: true, NavPathBudgetPatch.Apply); PatchRegistry.Register("TargetSearchGc", "Remove per-call allocations from target searches used by missiles, drones, turrets, and AI.", defaultOn: true, TargetSearchGcPatch.Apply); PatchRegistry.Register("DamageNumberBatch", "Batch damage-number particle writes to one copy per frame instead of one per number.", defaultOn: true, DamageNumberBatchPatch.Apply, DamageNumberBatchPatch.Unapply); PatchRegistry.Register("RaycastThrottle", "Throttle AI obstruction raycasts to the interval the game defines but never uses.", defaultOn: true, RaycastThrottlePatch.Apply); PatchRegistry.Register("HealthBarThrottle", "Restore the health-bar update throttle that the game defeats on load.", defaultOn: true, HealthBarThrottlePatch.Apply); PatchRegistry.Register("TeamItemCountCache", "Cache team item counts briefly. Removes repeated full scans of all characters during teleporter events.", defaultOn: true, TeamItemCountCachePatch.Apply, TeamItemCountCachePatch.Unapply); PatchRegistry.Register("ModelCameraPass", "One camera pass over character models instead of two, and a frame stride on the overlay scan. The base game measures hurtbox distances for models that draw nothing.", defaultOn: true, ModelCameraPassPatch.Apply); PatchRegistry.Register("PerfReadout", "Frame times and entity counts on screen and in the log.", defaultOn: true, PerfReadoutPatch.Apply, PerfReadoutPatch.Unapply); PatchRegistry.Register("FixedTimeCatchup", "Clamp physics catch-up so a slow frame cannot snowball into a freeze. The game slows down under extreme load instead.", defaultOn: true, FixedTimeCatchupPatch.Apply, FixedTimeCatchupPatch.Unapply); PatchRegistry.Register("SkipIntro", "Boot straight to the title screen without the startup video.", defaultOn: true, SkipIntroPatch.Apply, SkipIntroPatch.Unapply); PatchRegistry.Register("CritOverstack", "Crit chance above 100 percent converts into extra crit damage.", defaultOn: true, CritOverstackPatch.Apply); PatchRegistry.Register("BugFixes", "Guards for vanilla code that throws exceptions during normal play.", defaultOn: true, BugFixesPatch.Apply); PatchRegistry.Register("Sharing", "Item and money sharing between players. Off by default, because it changes the balance of the run for every player. Host-side, clients need no mod.", defaultOn: false, SharingPatch.Apply); PatchRegistry.Register("EmptyChests", "Remove opened chests, used barrels, and spent multishops after a delay.", defaultOn: true, EmptyChestsPatch.Apply, EmptyChestsPatch.Unapply); PatchRegistry.Register("AutoSprint", "Sprint turns on by itself while moving.", defaultOn: true, AutoSprintPatch.Apply, AutoSprintPatch.Unapply); PatchRegistry.Register("DpsMeter", "Damage per player for the stage you are on. Shows every connected player.", defaultOn: true, DpsMeterPatch.Apply, DpsMeterPatch.Unapply); PatchRegistry.Register("StatPoints", "Earn points as your character levels and spend them on stats. Works with every survivor.", defaultOn: true, StatPointsPatch.Apply, StatPointsPatch.Unapply); } } internal static class PatchRegistry { internal delegate void GroupApplier(Harmony harmony); internal delegate void GroupReverter(); internal sealed class Group { internal string Key; internal string Description; internal bool DefaultOn; internal GroupApplier Apply; internal GroupReverter Revert; internal ConfigEntry Toggle; internal Harmony Harmony; internal bool Active; internal string Error; } private static readonly List groups = new List(); internal static IReadOnlyList Groups => groups; internal static void Register(string key, string description, bool defaultOn, GroupApplier apply, GroupReverter revert = null) { groups.Add(new Group { Key = key, Description = description, DefaultOn = defaultOn, Apply = apply, Revert = revert }); } internal static int ApplyAll() { PatchGroups.RegisterAll(); int num = 0; foreach (Group group in groups) { group.Toggle = Plugin.Cfg.Bind("Features", group.Key, group.DefaultOn, group.Description); Group captured = group; captured.Toggle.SettingChanged += delegate { if (captured.Toggle.Value) { Enable(captured); } else { Disable(captured); } }; if (group.Toggle.Value && Enable(group)) { num++; } } return num; } internal static bool Enable(Group group) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected O, but got Unknown if (group.Active) { return true; } group.Harmony = new Harmony("com.tim.LateGamePerformance." + group.Key); try { group.Apply(group.Harmony); group.Active = true; group.Error = null; return true; } catch (Exception ex) { group.Error = ex.Message; Plugin.Log.LogError((object)$"Feature '{group.Key}' failed and is off: {ex}"); try { group.Harmony.UnpatchSelf(); } catch (Exception) { } group.Harmony = null; return false; } } internal static void Disable(Group group) { if (group.Active) { try { group.Revert?.Invoke(); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Feature '" + group.Key + "' did not clean up: " + ex.Message)); } Harmony harmony = group.Harmony; if (harmony != null) { harmony.UnpatchSelf(); } group.Harmony = null; group.Active = false; } } internal static void SetEnabled(Group group, bool enabled) { if (group.Toggle != null) { group.Toggle.Value = enabled; } if (enabled) { Enable(group); } else { Disable(group); } } internal static void DisableAll() { foreach (Group group in groups) { Disable(group); } } } internal sealed class PerfHud : MonoBehaviour { private const int SampleCount = 512; private const float PeakWindowSeconds = 10f; private const float Width = 290f; private const float Pad = 14f; private const float TitleHeight = 28f; private const float LineHeight = 22f; private static readonly string[] Titles = new string[8] { "Frame", "FPS", "Peak 10s", "Peak run", "Monsters", "Hurtboxes", "GC0 / s", "Heap" }; private readonly float[] samples = new float[512]; private int sampleIndex; private int sampleFill; private readonly float[] secondPeaks = new float[10]; private int lastSecond = -1; private float runPeakFrame; private int lastGcCount; private float gcStamp; private float gcPerSecond; private float nextLog; private bool wasRunning; private Canvas canvas; private HGTextMeshProUGUI[] values; internal static PerfHud Instance { get; private set; } internal static void Install(GameObject host) { Instance = host.AddComponent(); } private void Update() { //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) if (!PerfReadoutPatch.Active) { Show(visible: false); return; } if (PerfReadoutPatch.ToggleKey != null) { KeyboardShortcut value = PerfReadoutPatch.ToggleKey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { PerfReadoutPatch.ShowPanel.Value = !PerfReadoutPatch.ShowPanel.Value; } } Sample(); if (!PerfReadoutPatch.ShowPanel.Value || !Object.op_Implicit((Object)(object)Run.instance) || ModMenu.IsOpen || StatPointsWindow.IsOpen) { Show(visible: false); return; } GameSkin.Refresh(); if (GameSkin.Ready) { if ((Object)(object)canvas == (Object)null) { Build(); } Show(visible: true); WriteValues(); } } private void OnDestroy() { if ((Object)(object)canvas != (Object)null) { Object.Destroy((Object)(object)((Component)canvas).gameObject); } } private void Show(bool visible) { UiFactory.SetVisible(canvas, visible); } private void Sample() { float unscaledDeltaTime = Time.unscaledDeltaTime; samples[sampleIndex] = unscaledDeltaTime; sampleIndex = (sampleIndex + 1) % 512; if (sampleFill < 512) { sampleFill++; } float unscaledTime = Time.unscaledTime; RollPeaks(Mathf.FloorToInt(unscaledTime), unscaledDeltaTime); if (!Object.op_Implicit((Object)(object)Run.instance)) { runPeakFrame = 0f; } else if (unscaledDeltaTime > runPeakFrame) { runPeakFrame = unscaledDeltaTime; } if (unscaledTime - gcStamp >= 1f) { int num = GC.CollectionCount(0); gcPerSecond = (float)(num - lastGcCount) / (unscaledTime - gcStamp); lastGcCount = num; gcStamp = unscaledTime; } float value = PerfReadoutPatch.LogInterval.Value; bool flag = Object.op_Implicit((Object)(object)Run.instance); if (flag != wasRunning) { wasRunning = flag; nextLog = unscaledTime + 2f; } if (value > 0f && flag && unscaledTime >= nextLog) { nextLog = unscaledTime + value; Plugin.Log.LogInfo((object)LogLine()); } } private void Build() { //IL_004a: 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_0064: 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_0093: 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) canvas = UiFactory.CreateCanvas("LGP_PerfReadout", 99, interactive: false); RectTransform val = UiFactory.Panel(((Component)canvas).transform, "Panel"); float value = PerfReadoutPatch.PanelX.Value; float num = 1f - PerfReadoutPatch.PanelY.Value; val.anchorMin = new Vector2(value, num); val.anchorMax = new Vector2(value, num); val.pivot = new Vector2(value, num); val.anchoredPosition = Vector2.zero; val.sizeDelta = new Vector2(290f, 56f + (float)Titles.Length * 22f); UiFactory.Row(((TMP_Text)UiFactory.Label((Transform)(object)val, "Title", "PERFORMANCE", GameSkin.TextKind.Header, 20f, (TextAlignmentOptions)513)).rectTransform, 14f, 262f, 14f, 28f); values = (HGTextMeshProUGUI[])(object)new HGTextMeshProUGUI[Titles.Length]; for (int i = 0; i < Titles.Length; i++) { float y = 42f + (float)i * 22f; HGTextMeshProUGUI obj = UiFactory.Label((Transform)(object)val, "Label" + i, Titles[i], GameSkin.TextKind.Body, 16f, (TextAlignmentOptions)513); ((Graphic)obj).color = GameSkin.QuietColor; UiFactory.Row(((TMP_Text)obj).rectTransform, 14f, 130f, y, 22f); values[i] = UiFactory.Label((Transform)(object)val, "Value" + i, string.Empty, GameSkin.TextKind.Header, 17f, (TextAlignmentOptions)516); UiFactory.Row(((TMP_Text)values[i]).rectTransform, 144f, 132f, y, 22f); } } private void WriteValues() { float num = Average(); ((TMP_Text)values[0]).text = $"{num * 1000f:0.0} ms"; ((TMP_Text)values[1]).text = ((num > 0f) ? (1f / num) : 0f).ToString("0", CultureInfo.InvariantCulture); ((TMP_Text)values[2]).text = $"{PeakFrame() * 1000f:0.0} ms"; ((TMP_Text)values[3]).text = $"{runPeakFrame * 1000f:0.0} ms"; ((TMP_Text)values[4]).text = Monsters().ToString(CultureInfo.InvariantCulture); ((TMP_Text)values[5]).text = HurtBox.readOnlyBullseyesList.Count.ToString(CultureInfo.InvariantCulture); ((TMP_Text)values[6]).text = gcPerSecond.ToString("0.0", CultureInfo.InvariantCulture); ((TMP_Text)values[7]).text = $"{GC.GetTotalMemory(forceFullCollection: false) / 1048576} MB"; } private void RollPeaks(int second, float frame) { int num = secondPeaks.Length; if (lastSecond < 0) { lastSecond = second; } else if (second > lastSecond) { int num2 = second - lastSecond; if (num2 >= num) { Array.Clear(secondPeaks, 0, num); } else { for (int i = 1; i <= num2; i++) { secondPeaks[((lastSecond + i) % num + num) % num] = 0f; } } lastSecond = second; } int num3 = (second % num + num) % num; if (frame > secondPeaks[num3]) { secondPeaks[num3] = frame; } } private float PeakFrame() { float num = 0f; for (int i = 0; i < secondPeaks.Length; i++) { if (secondPeaks[i] > num) { num = secondPeaks[i]; } } return num; } private float Average() { if (sampleFill == 0) { return 0f; } float num = 0f; for (int i = 0; i < sampleFill; i++) { num += samples[i]; } return num / (float)sampleFill; } private static int Monsters() { return TeamComponent.GetTeamMembers((TeamIndex)2).Count; } private string LogLine() { float num = Average(); return string.Format(CultureInfo.InvariantCulture, "PERF stage {0} | frame avg {1:0.0} ms ({2:0} fps), peak {3:0.0} ms in {4:0} s, run peak {5:0.0} ms | bodies {6}, monsters {7}, models {8}, hurtboxes {9} | gc0 {10:0.0}/s, heap {11:0} MB", Clock(DpsMeterPatch.StageSeconds), num * 1000f, (num > 0f) ? (1f / num) : 0f, PeakFrame() * 1000f, 10f, runPeakFrame * 1000f, CharacterBody.readOnlyInstancesList.Count, Monsters(), InstanceTracker.GetInstancesList().Count, HurtBox.readOnlyBullseyesList.Count, gcPerSecond, GC.GetTotalMemory(forceFullCollection: false) / 1048576); } private static string Clock(float seconds) { int num = Mathf.Max(0, Mathf.FloorToInt(seconds)); return (num / 60).ToString(CultureInfo.InvariantCulture) + ":" + (num % 60).ToString("00", CultureInfo.InvariantCulture); } } [BepInPlugin("com.tim.LateGamePerformance", "LateGamePerformance", "1.5.0")] public sealed class Plugin : BaseUnityPlugin { public const string Guid = "com.tim.LateGamePerformance"; public const string Name = "LateGamePerformance"; public const string Version = "1.5.0"; internal static ManualLogSource Log; internal static ConfigFile Cfg; private Harmony coreHarmony; private void Awake() { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown //IL_00ab: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; Cfg = ((BaseUnityPlugin)this).Config; coreHarmony = new Harmony("com.tim.LateGamePerformance.core"); ModMenu.Install(coreHarmony, ((Component)this).gameObject); int num = PatchRegistry.ApplyAll(); StatPointsHud.Install(((Component)this).gameObject); DpsHud.Install(((Component)this).gameObject); StatPointsWindow.Install(((Component)this).gameObject); PerfHud.Install(((Component)this).gameObject); Log.LogInfo((object)string.Format("{0} {1}: {2} of {3} features active. Press {4} for the menu.", "LateGamePerformance", "1.5.0", num, PatchRegistry.Groups.Count, ModMenu.Hotkey.Value)); } private void OnDestroy() { ModMenu.Close(); PatchRegistry.DisableAll(); Harmony obj = coreHarmony; if (obj != null) { obj.UnpatchSelf(); } } } internal sealed class StatPointsHud : MonoBehaviour { [CompilerGenerated] private static class <>O { public static UnityAction <0>__Open; } private const float BadgeSize = 74f; private static ConfigEntry showBadge; private static ConfigEntry openKey; private static ConfigEntry badgeX; private static ConfigEntry badgeY; private Canvas canvas; private RectTransform badge; private HGTextMeshProUGUI count; private HGTextMeshProUGUI word; private Image backing; internal static StatPointsHud Instance { get; private set; } internal static void Install(GameObject host) { //IL_0038: 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) //IL_0083: Expected O, but got Unknown //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Expected O, but got Unknown showBadge = Plugin.Cfg.Bind("StatPoints", "ShowBadge", true, "Show a badge on screen while you have points to spend."); openKey = Plugin.Cfg.Bind("StatPoints", "OpenKey", new KeyboardShortcut((KeyCode)284, Array.Empty()), "Key that opens the stat points window."); badgeX = Plugin.Cfg.Bind("StatPoints", "BadgeX", 0.028f, new ConfigDescription("Badge position across the screen, 0 is left and 1 is right.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); badgeY = Plugin.Cfg.Bind("StatPoints", "BadgeY", 0.4f, new ConfigDescription("Badge position down the screen, 0 is top and 1 is bottom.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); Instance = host.AddComponent(); } private void Update() { //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_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) if (openKey != null) { KeyboardShortcut value = openKey.Value; if (((KeyboardShortcut)(ref value)).IsDown() && !ModMenu.IsOpen) { StatPointsWindow.Toggle(); return; } } int num = Available(); if (showBadge == null || !showBadge.Value || !StatPointsPatch.CanSpend || num <= 0 || !Object.op_Implicit((Object)(object)Run.instance) || ModMenu.IsOpen || StatPointsWindow.IsOpen) { UiFactory.SetVisible(canvas, visible: false); return; } GameSkin.Refresh(); if (GameSkin.Ready) { if ((Object)(object)canvas == (Object)null) { Build(); } UiFactory.SetVisible(canvas, visible: true); ((TMP_Text)count).text = num.ToString(); ((TMP_Text)word).text = ((num == 1) ? "point" : "points"); float num2 = 0.75f + 0.25f * Mathf.Sin(Time.unscaledTime * 3f); Color headerColor = GameSkin.HeaderColor; ((Graphic)backing).color = new Color(headerColor.r, headerColor.g, headerColor.b, 0.2f * num2); } } private void OnDestroy() { if ((Object)(object)canvas != (Object)null) { Object.Destroy((Object)(object)((Component)canvas).gameObject); } } private static int Available() { if (!StatPointsPatch.TryGetLocal(out var tracker, out var _)) { return 0; } return tracker.AvailablePoints(); } private void Build() { //IL_0054: 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_0078: 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_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_0207: Unknown result type (might be due to invalid IL or missing references) //IL_0245: Unknown result type (might be due to invalid IL or missing references) //IL_024a: Unknown result type (might be due to invalid IL or missing references) //IL_024d: Unknown result type (might be due to invalid IL or missing references) //IL_0252: Unknown result type (might be due to invalid IL or missing references) //IL_0272: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Expected O, but got Unknown canvas = UiFactory.CreateCanvas("LGP_StatBadge", 98, interactive: true); badge = UiFactory.Panel(((Component)canvas).transform, "Badge"); float value = badgeX.Value; float num = 1f - badgeY.Value; badge.anchorMin = new Vector2(value, num); badge.anchorMax = new Vector2(value, num); badge.pivot = new Vector2(value, num); badge.anchoredPosition = Vector2.zero; badge.sizeDelta = new Vector2(74f, 92f); backing = UiFactory.Fill((Transform)(object)badge, "Pulse", Color.clear); UiFactory.Stretch(((Graphic)backing).rectTransform, 3f, 3f); GameObject val = new GameObject("Open", new Type[3] { typeof(RectTransform), typeof(CanvasRenderer), typeof(Image) }); RectTransform component = val.GetComponent(); ((Transform)component).SetParent((Transform)(object)badge, false); UiFactory.Stretch(component); Image component2 = val.GetComponent(); ((Graphic)component2).color = new Color(0f, 0f, 0f, 0.01f); Button obj = val.AddComponent