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 Advance Features v1.3.0
BepInEx/plugins/advancedfeatures/AdvancedFeatures.dll
Decompiled a month agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Threading.Tasks; using AdvancedFeatures.NetcodePatcher; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using Dissonance; using GameNetcodeStuff; using HarmonyLib; using Microsoft.CodeAnalysis; using Steamworks; using Steamworks.Data; using TMPro; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] [module: NetcodePatchedAssembly] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.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 AdvancedFeatures { public static class CoronerOverridePerformanceReportPrefixPatch { private static MethodInfo getCauseOfDeathMethod; private static MethodInfo stringifyCauseOfDeathMethod; public static void TryPatch(Harmony harmony) { //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Expected O, but got Unknown try { Type type = AccessTools.TypeByName("Coroner.API"); if (type == null) { Plugin.Log.LogInfo((object)"Coroner API type not found, skipping Coroner patch"); return; } getCauseOfDeathMethod = AccessTools.Method(type, "GetCauseOfDeath", new Type[1] { typeof(PlayerControllerB) }, (Type[])null); stringifyCauseOfDeathMethod = AccessTools.Method(type, "StringifyCauseOfDeath", (Type[])null, (Type[])null); MethodInfo methodInfo = AccessTools.Method("Coroner.Patch.HUDManagerFillEndGameStatsPatch:OverridePerformanceReport", (Type[])null, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(typeof(CoronerOverridePerformanceReportPrefixPatch), "Prefix", (Type[])null, (Type[])null); if (methodInfo == null) { Plugin.Log.LogInfo((object)"Could not find Coroner OverridePerformanceReport"); return; } if (methodInfo2 == null) { Plugin.Log.LogError((object)"Could not find Coroner prefix method"); return; } harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Plugin.Log.LogInfo((object)"Applied Coroner compatibility patch"); } catch (Exception ex) { Plugin.Log.LogError((object)("Failed to patch Coroner compatibility: " + ex)); } } public static void Prefix(HUDManager __instance) { Plugin.Log.LogInfo((object)"Running before Coroner OverridePerformanceReport"); if ((Object)(object)StartOfRound.Instance == (Object)null || StartOfRound.Instance.allPlayerScripts == null) { Plugin.Log.LogError((object)"Coroner prefix: StartOfRound player list not ready"); return; } if (getCauseOfDeathMethod == null || stringifyCauseOfDeathMethod == null) { Plugin.Log.LogError((object)"Coroner methods not initialized"); return; } int num = StartOfRound.Instance.allPlayerScripts.Length; CoronerPatch.InitializeStorage(num); Random random = BuildSyncedRandom(); for (int i = 0; i < num; i++) { try { PlayerControllerB val = StartOfRound.Instance.allPlayerScripts[i]; if (!((Object)(object)val == (Object)null)) { object obj = getCauseOfDeathMethod.Invoke(null, new object[1] { val }); if (obj != null) { string stringifiedDeath = (string)stringifyCauseOfDeathMethod.Invoke(null, new object[2] { obj, random }); CoronerPatch.CoronerVariableInterceptor(i, obj.ToString(), stringifiedDeath); } } } catch (Exception ex) { Plugin.Log.LogError((object)("Error collecting Coroner death data for player " + i + ": " + ex)); } } } private static Random BuildSyncedRandom() { int randomMapSeed = StartOfRound.Instance.randomMapSeed; Plugin.Log.LogDebug((object)("Syncing randomization to map seed: '" + randomMapSeed + "'")); return new Random(randomMapSeed); } } public static class CoronerPatch { public class StoredDeathData { public string CauseOfDeath; public string StringifiedDeath; } public static StoredDeathData[] DeathData; public static void InitializeStorage(int playerCount) { DeathData = new StoredDeathData[playerCount]; } public static void CoronerVariableInterceptor(int playerIndex, string causeOfDeath, string stringifiedDeath) { if (DeathData != null && playerIndex >= 0 && playerIndex < DeathData.Length) { DeathData[playerIndex] = new StoredDeathData { CauseOfDeath = causeOfDeath, StringifiedDeath = stringifiedDeath }; } } } [HarmonyPatch] public class DeathScreen { public class SpectatorBox { public PlayerControllerB Player; public GameObject Container; public RawImage Avatar; public Animator Animator; public Text NameText; public Texture2D AvatarTexture; public float SmoothedVolume; } private static GameObject PlayerBoxPrefab; private static GridLayoutGroup GridLayout; private static readonly Dictionary<ulong, SpectatorBox> Spectators = new Dictionary<ulong, SpectatorBox>(); private static int _prevChildCount = -1; public static void LoadAssets(AssetBundle assets) { Plugin.Log.LogInfo((object)"Loading DeathScreen assets"); PlayerBoxPrefab = assets.LoadAsset<GameObject>("Assets/Prefabs/UI/PlayerBox.prefab"); if ((Object)(object)PlayerBoxPrefab == (Object)null) { Plugin.Log.LogError((object)"Failed to load PlayerBox prefab for DeathScreen"); } else if (Plugin.EnableAdvancedLogging.Value) { Plugin.Log.LogInfo((object)"DeathScreen assets loaded"); } } [HarmonyPatch(typeof(HUDManager), "Start")] [HarmonyPrefix] public static void Init(HUDManager __instance) { //IL_0076: 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_008e: 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_00a6: 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_00be: 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) //IL_00d6: 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_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_010e: 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_014e: Unknown result type (might be due to invalid IL or missing references) if (Plugin.EnableDeathUI.Value) { Plugin.Log.LogInfo((object)"Initializing DeathScreen UI"); if (Plugin.EnableAdvancedLogging.Value) { Plugin.Log.LogInfo((object)"Initializing DeathScreen"); } RectTransform component = ((Component)__instance.SpectateBoxesContainer).GetComponent<RectTransform>(); RectTransform component2 = ((Component)((Component)component).transform.parent).GetComponent<RectTransform>(); RectTransform component3 = ((Component)((Component)component2).transform.parent).GetComponent<RectTransform>(); component2.anchorMin = Vector2.zero; component2.anchorMax = Vector2.one; component2.offsetMin = Vector2.zero; component2.offsetMax = Vector2.zero; component3.anchorMin = Vector2.zero; component3.anchorMax = Vector2.one; component3.offsetMin = Vector2.zero; component3.offsetMax = Vector2.zero; component.anchorMin = Vector2.zero; component.anchorMax = new Vector2(1f, 0f); component.pivot = Vector2.zero; component.offsetMin = new Vector2(15f, 15f); component.offsetMax = new Vector2(-15f, 115f); GridLayout = ((Component)component).gameObject.AddComponent<GridLayoutGroup>(); GridLayout.spacing = new Vector2(5f, 0f); GridLayout.constraint = (Constraint)2; GridLayout.constraintCount = 1; GridLayout.startCorner = (Corner)2; ((LayoutGroup)GridLayout).childAlignment = (TextAnchor)6; } } [HarmonyPatch(typeof(HUDManager), "RemoveSpectateUI")] [HarmonyPrefix] public static void RemoveSpectateUI() { if (Plugin.EnableDeathUI.Value) { Plugin.Log.LogInfo((object)"Cleaning up DeathScreen UI"); if (Plugin.EnableAdvancedLogging.Value) { Plugin.Log.LogInfo((object)"Removing DeathScreen spectate UI"); } ulong[] array = Spectators.Keys.ToArray(); foreach (ulong id in array) { DestroySpectatorBox(id); } _prevChildCount = -1; } } [HarmonyPatch(typeof(HUDManager), "Update")] [HarmonyPrefix] private static void Update() { if (!Plugin.EnableDeathUI.Value || (Object)(object)StartOfRound.Instance.voiceChatModule == (Object)null) { return; } bool flag = false; List<ulong> list = new List<ulong>(); foreach (KeyValuePair<ulong, SpectatorBox> spectator in Spectators) { if ((Object)(object)spectator.Value.Container == (Object)null) { list.Add(spectator.Key); continue; } PlayerControllerB player = spectator.Value.Player; if (!player.isPlayerControlled && !player.isPlayerDead) { continue; } if ((Object)(object)player == (Object)(object)GameNetworkManager.Instance.localPlayerController) { if (!string.IsNullOrEmpty(StartOfRound.Instance.voiceChatModule.LocalPlayerName)) { VoicePlayerState val = StartOfRound.Instance.voiceChatModule.FindPlayer(StartOfRound.Instance.voiceChatModule.LocalPlayerName); if (val != null) { float num = ((StartOfRound.Instance.voiceChatModule.IsMuted || !val.IsSpeaking || val.Amplitude < 0.005f) ? 0f : (val.Amplitude * Plugin.DeathVoiceSensitivity.Value)); spectator.Value.SmoothedVolume = Mathf.Lerp(spectator.Value.SmoothedVolume, num, Time.unscaledDeltaTime * Plugin.BounceSmoothness.Value); spectator.Value.Animator.SetFloat("Volume", spectator.Value.SmoothedVolume); } } } else if (player.voicePlayerState == null) { if (!flag) { flag = true; StartOfRound.Instance.RefreshPlayerVoicePlaybackObjects(); } } else { VoicePlayerState voicePlayerState = player.voicePlayerState; float num2 = ((!voicePlayerState.IsSpeaking || voicePlayerState.IsLocallyMuted || voicePlayerState.Amplitude < 0.005f) ? 0f : (voicePlayerState.Amplitude / Mathf.Max(voicePlayerState.Volume, 0.01f) * Plugin.DeathVoiceSensitivity.Value)); spectator.Value.SmoothedVolume = Mathf.Lerp(spectator.Value.SmoothedVolume, num2, Time.unscaledDeltaTime * Plugin.BounceSmoothness.Value); spectator.Value.Animator.SetFloat("Volume", spectator.Value.SmoothedVolume); } } if (list.Count <= 0) { return; } foreach (ulong item in list) { DestroySpectatorBox(item); } UpdateLayoutSize(); } private static void UpdateLayoutSize() { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: 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) int num = ((Component)GridLayout).transform.childCount - 4; if (num != _prevChildCount && num > 0) { _prevChildCount = num; Rect rect = ((Component)GridLayout).GetComponent<RectTransform>().rect; float num2 = ((Rect)(ref rect)).width / (float)num; num2 -= 5f; if (num2 > 70f) { num2 = 70f; } GridLayout.cellSize = new Vector2(num2, num2); if (Plugin.EnableAdvancedLogging.Value) { Plugin.Log.LogInfo((object)$"Updated layout size: {num} boxes, width {num2}"); } } } [HarmonyPatch(typeof(HUDManager), "UpdateBoxesSpectateUI")] [HarmonyPrefix] public static bool UpdateBoxes(HUDManager __instance) { //IL_01b7: 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_0254: Expected O, but got Unknown //IL_02b6: Unknown result type (might be due to invalid IL or missing references) //IL_02bb: Unknown result type (might be due to invalid IL or missing references) //IL_02f5: Unknown result type (might be due to invalid IL or missing references) //IL_02fa: Unknown result type (might be due to invalid IL or missing references) //IL_0311: Unknown result type (might be due to invalid IL or missing references) //IL_0331: Unknown result type (might be due to invalid IL or missing references) //IL_0348: Unknown result type (might be due to invalid IL or missing references) //IL_035f: Unknown result type (might be due to invalid IL or missing references) //IL_0376: Unknown result type (might be due to invalid IL or missing references) //IL_038d: Unknown result type (might be due to invalid IL or missing references) //IL_0392: Unknown result type (might be due to invalid IL or missing references) //IL_03ad: Unknown result type (might be due to invalid IL or missing references) //IL_03de: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.EnableDeathUI.Value) { return true; } if (Plugin.EnableAdvancedLogging.Value) { Plugin.Log.LogInfo((object)"Updating death spectate boxes"); } if (Plugin.EnableAdvancedLogging.Value) { Plugin.Log.LogInfo((object)"Updating death spectate boxes - Advanced"); } if ((Object)(object)PlayerBoxPrefab == (Object)null) { Plugin.Log.LogError((object)"PlayerBox prefab missing, cannot update spectators"); return true; } for (int i = 0; i < StartOfRound.Instance.allPlayerScripts.Length; i++) { PlayerControllerB val = StartOfRound.Instance.allPlayerScripts[i]; if (!val.isPlayerDead) { if (!val.isPlayerControlled && Spectators.ContainsKey(val.playerClientId)) { DestroySpectatorBox(val.playerClientId); } continue; } if (Spectators.ContainsKey(val.playerClientId)) { if ((Object)(object)Spectators[val.playerClientId].Container == (Object)null) { DestroySpectatorBox(val.playerClientId); } else if (!Spectators[val.playerClientId].Container.activeSelf) { Spectators[val.playerClientId].Container.SetActive(true); } continue; } GameObject val2 = Object.Instantiate<GameObject>(PlayerBoxPrefab, __instance.SpectateBoxesContainer, false); if (Plugin.EnableAdvancedLogging.Value) { Plugin.Log.LogInfo((object)("Created spectator box for " + val.playerUsername)); } val2.transform.localScale = Vector3.one; val2.SetActive(true); SpectatorBox spectatorBox = new SpectatorBox { Container = val2, Animator = val2.GetComponent<Animator>(), Player = val, Avatar = ((Component)val2.transform.GetChild(0).GetChild(2)).GetComponent<RawImage>() }; Spectators[val.playerClientId] = spectatorBox; if (Plugin.ShowDeathUsername.Value) { GameObject val3 = new GameObject("NameText", new Type[1] { typeof(RectTransform) }); val3.transform.SetParent(val2.transform, false); Text val4 = val3.AddComponent<Text>(); val4.text = val.playerUsername; val4.font = Resources.GetBuiltinResource<Font>("Arial.ttf"); val4.fontSize = 14; val4.alignment = (TextAnchor)4; ((Graphic)val4).color = Color32.op_Implicit(new Color32(byte.MaxValue, (byte)75, (byte)54, byte.MaxValue)); val4.resizeTextForBestFit = true; val4.resizeTextMinSize = 8; val4.resizeTextMaxSize = 14; Outline val5 = val3.AddComponent<Outline>(); ((Shadow)val5).effectColor = Color32.op_Implicit(new Color32((byte)0, (byte)0, (byte)0, byte.MaxValue)); ((Shadow)val5).effectDistance = new Vector2(0.7f, -0.7f); RectTransform component = val3.GetComponent<RectTransform>(); component.anchorMin = new Vector2(0f, 1f); component.anchorMax = new Vector2(1f, 1f); component.pivot = new Vector2(0.5f, 1f); component.sizeDelta = new Vector2(0f, 20f); Rect rect = ((Graphic)spectatorBox.Avatar).rectTransform.rect; float height = ((Rect)(ref rect)).height; component.anchoredPosition = new Vector2(0f, 0f - (height + 78f)); spectatorBox.NameText = val4; } if (!GameNetworkManager.Instance.disableSteam) { FillImageWithSteamProfile(spectatorBox, SteamId.op_Implicit(val.playerSteamId)); } } UpdateLayoutSize(); return false; } private static void DestroySpectatorBox(ulong id) { if (!Spectators.TryGetValue(id, out var value)) { Plugin.Log.LogError((object)$"Tried to destroy missing spectator box {id}"); return; } if (Plugin.EnableAdvancedLogging.Value) { Plugin.Log.LogInfo((object)$"Destroying spectator box for {id}"); } if ((Object)(object)value.AvatarTexture != (Object)null) { Object.Destroy((Object)(object)value.AvatarTexture); value.AvatarTexture = null; } if ((Object)(object)value.Container != (Object)null) { Object.Destroy((Object)(object)value.Container); } Spectators.Remove(id); if (Plugin.EnableAdvancedLogging.Value) { Plugin.Log.LogInfo((object)$"Spectator box removed. Remaining: {Spectators.Count}"); } } private static async Task FillImageWithSteamProfile(SpectatorBox box, SteamId steamId) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) if (!SteamClient.IsValid) { return; } if (Plugin.EnableAdvancedLogging.Value) { Plugin.Log.LogInfo((object)$"Loading avatar for {steamId}"); } Image? steamImg = await SteamFriends.GetLargeAvatarAsync(steamId); if (!steamImg.HasValue) { Plugin.Log.LogError((object)$"Steam avatar not found for {steamId}"); return; } int w = (int)steamImg.Value.Width; int h = (int)steamImg.Value.Height; Texture2D tex = new Texture2D(w, h, (TextureFormat)4, false); bool loaded = false; byte[] data = steamImg.Value.Data; if (data != null && data.Length == w * h * 4) { try { tex.LoadRawTextureData(data); loaded = true; } catch (Exception ex) { Plugin.Log.LogError((object)("Failed to load avatar texture data: " + ex)); } } if (!loaded) { Color32[] pixels = (Color32[])(object)new Color32[w * h]; for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { Image value = steamImg.Value; Color p = ((Image)(ref value)).GetPixel(x, y); pixels[(h - y - 1) * w + x] = new Color32(p.r, p.g, p.b, p.a); } } tex.SetPixels32(pixels); } tex.Apply(); if ((Object)(object)box.AvatarTexture != (Object)null) { Object.Destroy((Object)(object)box.AvatarTexture); } box.AvatarTexture = tex; box.Avatar.texture = (Texture)(object)tex; box.Avatar.uvRect = new Rect(0f, 1f, 1f, -1f); if (Plugin.EnableAdvancedLogging.Value) { Plugin.Log.LogInfo((object)$"Avatar loaded for {steamId}"); } } } [HarmonyPatch] public class Endscreen { [HarmonyPatch(typeof(Animator), "SetTrigger", new Type[] { typeof(string) })] private static class Animator_SetTrigger_Patch { [HarmonyPostfix] private static void Postfix(Animator __instance, string name) { if (Plugin.EnablePerformanceUI.Value && name == "displayStats" && (Object)(object)__instance == (Object)(object)HUDManager.Instance.endgameStatsAnimator) { if (Plugin.EnableAdvancedLogging.Value) { Plugin.Log.LogInfo((object)"Animator trigger received; opening end screen"); } Open(); } } } private static readonly MethodInfo _setTrigger = AccessTools.Method(typeof(Animator), "SetTrigger", new Type[1] { typeof(string) }, (Type[])null); private static GameObject Container; private static GameObject PerformanceReportPrefab; private static GameObject DeadContainerPrefab; private static GameObject MissingContainerPrefab; private static GameObject NoteContainerPrefab; private static Transform AllDead; private static Transform PlayerNoteContainer; private static Transform DeadNoteContainer; private static Transform MissingTitle; private static Transform MissingScrollBox; private static Transform MissingNoteContainer; private static Transform CollectedLabel; private static Transform CollectedLine; private static TextMeshProUGUI CollectedText; private static TextMeshProUGUI TotalText; private static Transform ScrapLost; private static TextMeshProUGUI ScrapLostText; private static TextMeshProUGUI GradeText; private static int CollectedScrap; private static int TotalScrap; private static string Grade; private static bool AreAllDead; private static int _playerNoteIndex; private static int _deadNoteIndex; private static int _missingNoteIndex; private static readonly WaitForEndOfFrame WaitFrame = new WaitForEndOfFrame(); private static GameObject GetOrCreate(Transform container, GameObject prefab, ref int index) { GameObject val = ((index >= container.childCount) ? Object.Instantiate<GameObject>(prefab, container) : ((Component)container.GetChild(index)).gameObject); val.SetActive(true); index++; return val; } public static void LoadAssets(AssetBundle assets) { Plugin.Log.LogInfo((object)"Loading Endscreen assets"); PerformanceReportPrefab = assets.LoadAsset<GameObject>("Assets/Prefabs/UI/PerformanceReport.prefab"); DeadContainerPrefab = assets.LoadAsset<GameObject>("Assets/Prefabs/UI/DeadContainer.prefab"); MissingContainerPrefab = assets.LoadAsset<GameObject>("Assets/Prefabs/UI/MissingContainer.prefab"); NoteContainerPrefab = assets.LoadAsset<GameObject>("Assets/Prefabs/UI/NoteContainer.prefab"); if ((Object)(object)PerformanceReportPrefab == (Object)null || (Object)(object)DeadContainerPrefab == (Object)null || (Object)(object)MissingContainerPrefab == (Object)null || (Object)(object)NoteContainerPrefab == (Object)null) { Plugin.Log.LogError((object)"Failed to load one or more Endscreen prefabs"); } else if (Plugin.EnableAdvancedLogging.Value) { Plugin.Log.LogInfo((object)"Endscreen assets loaded"); Plugin.Log.LogInfo((object)$"Prefabs: report={(Object)(object)PerformanceReportPrefab != (Object)null}, dead={(Object)(object)DeadContainerPrefab != (Object)null}, missing={(Object)(object)MissingContainerPrefab != (Object)null}, note={(Object)(object)NoteContainerPrefab != (Object)null}"); } } public static void Open() { if (!Plugin.EnablePerformanceUI.Value) { return; } Plugin.Log.LogInfo((object)"Opening custom performance report screen"); if (Plugin.EnableAdvancedLogging.Value) { Plugin.Log.LogInfo((object)"Open() was called."); } try { Transform transform = ((Component)HUDManager.Instance.endgameStatsAnimator).gameObject.transform; for (int i = 0; i < transform.childCount; i++) { Transform child = transform.GetChild(i); if (((Object)child).name == "Text") { ((Component)child).gameObject.SetActive(false); } if (((Object)child).name == "BGBoxes" || ((Object)child).name == "Lines") { Object.Destroy((Object)(object)((Component)child).gameObject); } } bool active = false; _playerNoteIndex = 0; _deadNoteIndex = 0; _missingNoteIndex = 0; int childCount = PlayerNoteContainer.childCount; for (int j = 0; j < childCount; j++) { ((Component)PlayerNoteContainer.GetChild(j)).gameObject.SetActive(false); } childCount = DeadNoteContainer.childCount; for (int k = 0; k < childCount; k++) { ((Component)DeadNoteContainer.GetChild(k)).gameObject.SetActive(false); } childCount = MissingNoteContainer.childCount; for (int l = 0; l < childCount; l++) { ((Component)MissingNoteContainer.GetChild(l)).gameObject.SetActive(false); } for (int m = 0; m < StartOfRound.Instance.allPlayerScripts.Length; m++) { PlayerControllerB val = StartOfRound.Instance.allPlayerScripts[m]; if (val.disconnectedMidGame) { continue; } string text = ((TMP_Text)HUDManager.Instance.statsUIElements.playerNamesText[m]).text; Texture2D val2 = null; string text2 = ((TMP_Text)HUDManager.Instance.statsUIElements.playerNotesText[m]).text; if (text2.StartsWith("Notes:")) { text2 = text2.Substring(6); } int num = text2.IndexOf("Cause of Death:", StringComparison.OrdinalIgnoreCase); if (num > -1) { text2 = text2.Substring(0, num); } text2 = text2.Trim(); string text3 = ""; try { text3 = ((object)(CauseOfDeath)(ref val.causeOfDeath)).ToString(); if (Chainloader.PluginInfos.ContainsKey("com.elitemastereric.coroner")) { if (CoronerPatch.DeathData != null && m >= 0 && m < CoronerPatch.DeathData.Length && CoronerPatch.DeathData[m] != null) { text3 = CoronerPatch.DeathData[m].StringifiedDeath ?? text3; } } else if (Plugin.EnableAdvancedLogging.Value) { Plugin.Log.LogInfo((object)("[Vanilla] " + text + " died of: " + text3)); } } catch (Exception ex) { Plugin.Log.LogError((object)("Coroner death lookup failed: " + ex)); text3 = ((object)(CauseOfDeath)(ref val.causeOfDeath)).ToString(); } bool flag = (Object)(object)HUDManager.Instance.statsUIElements.playerStates[m].sprite == (Object)(object)HUDManager.Instance.statsUIElements.deceasedIcon; bool flag2 = (Object)(object)HUDManager.Instance.statsUIElements.playerStates[m].sprite == (Object)(object)HUDManager.Instance.statsUIElements.missingIcon; if (!string.IsNullOrEmpty(text2) && !flag && !flag2) { AddPlayerNote(val.playerSteamId, text, text2); } if ((Object)(object)HUDManager.Instance.statsUIElements.playerStates[m].sprite == (Object)(object)HUDManager.Instance.statsUIElements.deceasedIcon) { AddDeceasedNote(val.playerSteamId, text, text3); } if ((Object)(object)HUDManager.Instance.statsUIElements.playerStates[m].sprite == (Object)(object)HUDManager.Instance.statsUIElements.missingIcon) { active = true; AddMissingNote(val.playerSteamId, text); } } ((Component)MissingTitle).gameObject.SetActive(active); ((Component)MissingScrollBox).gameObject.SetActive(active); CollectedScrap = RoundManager.Instance.scrapCollectedInLevel; TotalScrap = (int)RoundManager.Instance.totalScrapValueInLevel; AreAllDead = ((Behaviour)HUDManager.Instance.statsUIElements.allPlayersDeadOverlay).enabled; ((Component)AllDead).gameObject.SetActive(AreAllDead); ((TMP_Text)CollectedText).text = string.Empty; ((TMP_Text)TotalText).text = string.Empty; ((Component)CollectedText).gameObject.SetActive(true); ((Component)TotalText).gameObject.SetActive(true); ((Component)CollectedLine).gameObject.SetActive(true); ((Component)CollectedLabel).gameObject.SetActive(true); ((Component)ScrapLost).gameObject.SetActive(false); Grade = ((TMP_Text)HUDManager.Instance.statsUIElements.gradeLetter).text; ((TMP_Text)GradeText).text = string.Empty; Plugin.Log.LogInfo((object)$"Scrap collected: {CollectedScrap}/{TotalScrap} - Grade {Grade}"); Container.SetActive(true); LayoutRebuilder.ForceRebuildLayoutImmediate(Container.GetComponent<RectTransform>()); ((MonoBehaviour)HUDManager.Instance).StartCoroutine(AnimateMenu()); if (Plugin.ForceQuit.Value > 0f) { ((MonoBehaviour)HUDManager.Instance).StartCoroutine(ForceCloseAfterTime(Plugin.ForceQuit.Value)); } if (Plugin.EnableAdvancedLogging.Value) { Plugin.Log.LogInfo((object)"End screen animation coroutine started"); } Plugin.Log.LogInfo((object)"Performance report screen displayed"); } catch (Exception ex2) { Plugin.Log.LogError((object)"Error occurred while opening end screen!"); Plugin.Log.LogError((object)ex2); } } private static IEnumerator ForceCloseAfterTime(float seconds) { if (Plugin.EnableAdvancedLogging.Value) { Plugin.Log.LogInfo((object)$"Force close timer started ({seconds} seconds)"); } yield return (object)new WaitForSeconds(seconds); if ((Object)(object)Container != (Object)null && Container.activeSelf) { Plugin.Log.LogInfo((object)"Force closing performance report after timeout"); Container.SetActive(false); Cursor.lockState = (CursorLockMode)1; Cursor.visible = false; } } private static IEnumerator AnimateMenu() { if (Plugin.EnableAdvancedLogging.Value) { Plugin.Log.LogInfo((object)"Animating performance report UI"); } Cursor.lockState = (CursorLockMode)2; Cursor.visible = !Plugin.EnablePerformanceReportCameraScroll.Value; yield return (object)new WaitForSeconds(1f); float p = 0f; ((TMP_Text)TotalText).text = TotalScrap.ToString(); while (p < 1f) { ((TMP_Text)CollectedText).text = CollectedScrap.ToString(); p += 0.05f; yield return WaitFrame; } if (AreAllDead) { yield return (object)new WaitForSeconds(1f); ((Component)CollectedText).gameObject.SetActive(false); ((Component)TotalText).gameObject.SetActive(false); ((Component)CollectedLine).gameObject.SetActive(false); ((Component)CollectedLabel).gameObject.SetActive(false); ((TMP_Text)ScrapLostText).text = "Lost 100% scrap"; ((Component)ScrapLost).gameObject.SetActive(true); } yield return (object)new WaitForSeconds(1f); ((TMP_Text)GradeText).text = Grade; yield return (object)new WaitForSeconds(5.5f - (AreAllDead ? 1f : 0f)); Container.SetActive(false); if (Plugin.EnableAdvancedLogging.Value) { Plugin.Log.LogInfo((object)"Performance report UI closed"); } Cursor.lockState = (CursorLockMode)1; Cursor.visible = false; } private static void AddPlayerNote(ulong steamId, string username, string notes) { //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) GameObject orCreate = GetOrCreate(PlayerNoteContainer, NoteContainerPrefab, ref _playerNoteIndex); if (Plugin.EnableAdvancedLogging.Value) { Plugin.Log.LogInfo((object)("Adding player note for " + username)); } Transform child = orCreate.transform.GetChild(0); RawImage component = ((Component)child.GetChild(0)).GetComponent<RawImage>(); if (Plugin.ShowAvatars.Value) { HUDManager.FillImageWithSteamProfile(component, SteamId.op_Implicit(steamId), true); ((Component)component).gameObject.SetActive(true); } else { component.texture = null; component.uvRect = new Rect(0f, 0f, 1f, 1f); ((Component)component).gameObject.SetActive(false); } TextMeshProUGUI component2 = ((Component)child.GetChild(1)).GetComponent<TextMeshProUGUI>(); ((TMP_Text)component2).text = username; ((TMP_Text)component2).fontSize = 36f; ((TMP_Text)((Component)orCreate.transform.GetChild(1)).GetComponent<TextMeshProUGUI>()).text = notes; orCreate.transform.localScale = Vector3.one; } private static void AddDeceasedNote(ulong steamId, string username, string deathReason) { //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) GameObject orCreate = GetOrCreate(DeadNoteContainer, DeadContainerPrefab, ref _deadNoteIndex); if (Plugin.EnableAdvancedLogging.Value) { Plugin.Log.LogInfo((object)("Adding deceased note for " + username)); } Transform child = orCreate.transform.GetChild(0); RawImage component = ((Component)child.GetChild(0)).GetComponent<RawImage>(); if (Plugin.ShowAvatars.Value) { HUDManager.FillImageWithSteamProfile(component, SteamId.op_Implicit(steamId), true); ((Component)component).gameObject.SetActive(true); } else { component.texture = null; component.uvRect = new Rect(0f, 0f, 1f, 1f); ((Component)component).gameObject.SetActive(false); } ((TMP_Text)((Component)child.GetChild(1)).GetComponent<TextMeshProUGUI>()).text = username; TextMeshProUGUI component2 = ((Component)orCreate.transform.GetChild(1)).GetComponent<TextMeshProUGUI>(); ((TMP_Text)component2).text = "* " + deathReason; ((Graphic)component2).color = Color32.op_Implicit(new Color32(byte.MaxValue, (byte)51, (byte)1, byte.MaxValue)); ((TMP_Text)component2).fontSize = 21.31f; orCreate.transform.localScale = Vector3.one; } private static void AddMissingNote(ulong steamId, string username) { //IL_00a1: 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_00d3: Unknown result type (might be due to invalid IL or missing references) GameObject orCreate = GetOrCreate(MissingNoteContainer, MissingContainerPrefab, ref _missingNoteIndex); if (Plugin.EnableAdvancedLogging.Value) { Plugin.Log.LogInfo((object)("Adding missing note for " + username)); } Transform child = orCreate.transform.GetChild(0); RawImage component = ((Component)child.GetChild(0)).GetComponent<RawImage>(); if (Plugin.ShowAvatars.Value) { HUDManager.FillImageWithSteamProfile(component, SteamId.op_Implicit(steamId), true); ((Component)component).gameObject.SetActive(true); } else { component.texture = null; component.uvRect = new Rect(0f, 0f, 1f, 1f); ((Component)component).gameObject.SetActive(false); } ((TMP_Text)((Component)child.GetChild(1)).GetComponent<TextMeshProUGUI>()).text = username; orCreate.transform.localScale = Vector3.one; } [HarmonyPatch(typeof(HUDManager), "Start")] [HarmonyPostfix] public static void Attach(HUDManager __instance) { //IL_0071: 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) if (Plugin.EnablePerformanceUI.Value) { if (Plugin.EnableAdvancedLogging.Value) { Plugin.Log.LogInfo((object)"Endscreen.Attach postfix invoked"); } Container = Object.Instantiate<GameObject>(PerformanceReportPrefab, ((Component)__instance.endgameStatsAnimator).transform.parent); Container.transform.localScale = new Vector3(0.5f, 0.5f, 0.5f); Container.GetComponent<RectTransform>().sizeDelta = new Vector2(823f, 717f); Transform child = Container.transform.GetChild(1); Transform child2 = child.GetChild(0); Transform child3 = child.GetChild(1); Transform child4 = Container.transform.GetChild(2); AllDead = child2.GetChild(1); PlayerNoteContainer = child2.GetChild(2).GetChild(0).GetChild(0); DeadNoteContainer = child3.GetChild(1).GetChild(0).GetChild(0); MissingTitle = child3.GetChild(2); MissingScrollBox = child3.GetChild(3); MissingNoteContainer = MissingScrollBox.GetChild(0).GetChild(0); ScrollRect componentInChildren = ((Component)MissingScrollBox).GetComponentInChildren<ScrollRect>(); Transform child5 = child4.GetChild(0); CollectedLabel = child5.GetChild(0); CollectedText = ((Component)child5.GetChild(1)).GetComponent<TextMeshProUGUI>(); CollectedLine = child5.GetChild(2); TotalText = ((Component)child5.GetChild(3)).GetComponent<TextMeshProUGUI>(); ScrapLost = child5.GetChild(4); ScrapLostText = ((Component)ScrapLost.GetChild(0)).GetComponent<TextMeshProUGUI>(); GradeText = ((Component)child4.GetChild(1).GetChild(1)).GetComponent<TextMeshProUGUI>(); Container.SetActive(false); } } } [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInPlugin("com.example.Advancedfeatures", "Advanced Features", "1.3.0")] public class Plugin : BaseUnityPlugin { public static ConfigEntry<bool> EnablePerformanceUI; public static ConfigEntry<bool> EnableDeathUI; public static ConfigEntry<bool> ShowDeathUsername; public static ConfigEntry<float> DeathVoiceSensitivity; public static ConfigEntry<float> BounceSmoothness; public static ConfigEntry<bool> ShowAvatars; public static ConfigEntry<bool> EnableAdvancedLogging; public static ConfigEntry<bool> EnablePerformanceReportCameraScroll; public static ConfigEntry<float> ForceQuit; public static bool CoronerInstalled; internal static ManualLogSource Log; private Harmony _harmony; private AssetBundle _assetBundle; private void Awake() { //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; Log.LogInfo((object)"Initializing Advanced Features plugin"); CoronerInstalled = Chainloader.PluginInfos.ContainsKey("com.elitemastereric.coroner"); Log.LogInfo((object)("Coroner installed: " + CoronerInstalled)); EnablePerformanceUI = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnablePerformanceReportUI", true, "Toggle the custom performance-report UI"); EnableDeathUI = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableDeathSpectateUI", true, "Toggle the custom death-spectate UI"); ShowDeathUsername = ((BaseUnityPlugin)this).Config.Bind<bool>("DeathScreen", "ShowUsernameUnderAvatar", true, "Enable or disable the player?s name under their avatar on the death spectate screen"); DeathVoiceSensitivity = ((BaseUnityPlugin)this).Config.Bind<float>("DeathScreen", "VoiceSensitivity", 10f, "How strongly avatars bounce in response to voice"); BounceSmoothness = ((BaseUnityPlugin)this).Config.Bind<float>("DeathScreen", "BounceSmoothness", 12f, "How quickly the avatar bounce reacts to voice volume. Higher = snappier bounce."); ShowAvatars = ((BaseUnityPlugin)this).Config.Bind<bool>("Performance Report UI", "ShowAvatars", false, "If true, fetch and display each player's Steam avatar on the performance report."); EnableAdvancedLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Logging", "EnableAdvancedLogging", false, "If true, logs when the mod does anything"); EnablePerformanceReportCameraScroll = ((BaseUnityPlugin)this).Config.Bind<bool>("Performance Report UI", "EnableCameraScroll", false, "If true, hides cursor and enables scroll wheel for all lists during performance report"); ForceQuit = ((BaseUnityPlugin)this).Config.Bind<float>("Performance Report UI", "ForceQuit", -1f, "Set to -1 to disable, any value above will be a timer for how long the game should wait before quitting. Useful if performance report gets stuck for you."); if (EnableAdvancedLogging.Value) { Log.LogInfo((object)"Advanced logging enabled"); } _harmony = new Harmony("com.example.Advancedfeatures"); _harmony.PatchAll(); Log.LogInfo((object)"Harmony patches applied"); if (CoronerInstalled) { CoronerOverridePerformanceReportPrefixPatch.TryPatch(_harmony); } else { Log.LogInfo((object)"Skipping Coroner compatibility patch because Coroner is not installed"); } string text = Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location), "advancedfeaturesassets"); try { if (File.Exists(text)) { Log.LogInfo((object)"Loading asset bundle for Advanced Features"); _assetBundle = AssetBundle.LoadFromFile(text); Endscreen.LoadAssets(_assetBundle); DeathScreen.LoadAssets(_assetBundle); Log.LogInfo((object)("Asset bundle has been found at " + text)); } else { Log.LogWarning((object)("Asset bundle not found at " + text)); } } catch (Exception ex) { Log.LogError((object)"Failed to load asset bundle"); Log.LogError((object)ex); } } } } namespace __GEN { internal class NetworkVariableSerializationHelper { [RuntimeInitializeOnLoadMethod] internal static void InitializeSerialization() { } } } namespace AdvancedFeatures.NetcodePatcher { [AttributeUsage(AttributeTargets.Module)] internal class NetcodePatchedAssemblyAttribute : Attribute { } }