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 ReidrheimrCustomMainMenu v1.0.1
CustomMainMenu.dll
Decompiled 2 days ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.Audio; using UnityEngine.Events; using UnityEngine.Networking; using UnityEngine.Rendering; using UnityEngine.SceneManagement; using UnityEngine.UI; using Valheim.SettingsGui; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("CustomMainMenu")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Custom main menu logo, music, loading art, Discord link, optional loading tips, and optional skip of the 1.0 startup intro.")] [assembly: AssemblyFileVersion("1.5.0.0")] [assembly: AssemblyInformationalVersion("1.5.0")] [assembly: AssemblyProduct("CustomMainMenu")] [assembly: AssemblyTitle("CustomMainMenu")] [assembly: AssemblyVersion("1.5.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 CustomMainMenu { internal static class ConfigLayout { internal static readonly string[] SectionOrder = new string[6] { "General", "Startup", "Main Menu", "Loading Screen", "Changelog", "Discord Panel" }; internal static bool IsOurs(ConfigFile config) { CustomMainMenuPlugin instance = CustomMainMenuPlugin.Instance; if ((Object)(object)instance != (Object)null) { return config == ((BaseUnityPlugin)instance).Config; } return false; } internal static bool TryWrite(ConfigFile config) { try { byte[] array = Build(config); string configFilePath = config.ConfigFilePath; if (File.Exists(configFilePath) && BytesEqual(File.ReadAllBytes(configFilePath), array)) { return true; } string directoryName = Path.GetDirectoryName(configFilePath); if (!string.IsNullOrEmpty(directoryName)) { Directory.CreateDirectory(directoryName); } File.WriteAllBytes(configFilePath, array); return true; } catch (Exception ex) { CustomMainMenuPlugin.LogWarn("Config could not be written in section order: " + ex.Message); return false; } } private static byte[] Build(ConfigFile config) { Dictionary<string, List<ConfigEntryBase>> dictionary = new Dictionary<string, List<ConfigEntryBase>>(StringComparer.Ordinal); foreach (KeyValuePair<ConfigDefinition, ConfigEntryBase> item in config) { if (!dictionary.TryGetValue(item.Key.Section, out var value)) { value = new List<ConfigEntryBase>(); dictionary[item.Key.Section] = value; } value.Add(item.Value); } using MemoryStream memoryStream = new MemoryStream(); using (StreamWriter streamWriter = new StreamWriter(memoryStream, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false))) { streamWriter.NewLine = "\r\n"; streamWriter.WriteLine("## Settings file was created by plugin Custom Main Menu v1.5.0"); streamWriter.WriteLine("## Plugin GUID: rdmods.custommainmenu"); streamWriter.WriteLine(); HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); for (int i = 0; i < SectionOrder.Length; i++) { string text = SectionOrder[i]; if (dictionary.TryGetValue(text, out var value2)) { WriteSection(streamWriter, text, value2); hashSet.Add(text); } } foreach (KeyValuePair<string, List<ConfigEntryBase>> item2 in dictionary) { if (!hashSet.Contains(item2.Key)) { WriteSection(streamWriter, item2.Key, item2.Value); } } } return memoryStream.ToArray(); } private static void WriteSection(StreamWriter writer, string section, List<ConfigEntryBase> entries) { writer.WriteLine("[" + section + "]"); writer.WriteLine(); for (int i = 0; i < entries.Count; i++) { ConfigEntryBase val = entries[i]; val.WriteDescription(writer); writer.WriteLine(val.Definition.Key + " = " + val.GetSerializedValue()); writer.WriteLine(); } } private static bool BytesEqual(byte[] a, byte[] b) { if (a.Length != b.Length) { return false; } for (int i = 0; i < a.Length; i++) { if (a[i] != b[i]) { return false; } } return true; } } [BepInPlugin("rdmods.custommainmenu", "Custom Main Menu", "1.5.0")] public sealed class CustomMainMenuPlugin : BaseUnityPlugin { [CompilerGenerated] private static class <>O { public static UnityAction <0>__OnDiscordJoinClicked; } public const string ModGuid = "rdmods.custommainmenu"; public const string ModName = "Custom Main Menu"; public const string ModVersion = "1.5.0"; public const string AssetFolderName = "CustomMainMenu"; public const string LoadingArtFolderName = "LoadingArt"; public const string PluginDefaultsFolderName = "defaults"; public const string HowToCustomizeFileName = "README-HOW-TO-CUSTOMIZE.txt"; public const int LoadingArtMaxSlot = 12; public const string DiscordTitleText = "Need help? Join Discord!"; public const string DiscordButtonText = "Click here to join Discord!"; internal static volatile bool MainMenuActive; private static ConfigEntry<bool>? _enableLogging; private Harmony? _harmony; private bool _inactive; private Coroutine? _atmosphereRoutine; private Coroutine? _discordLogoRoutine; private static GameObject? _discordPanel; private static GameObject? _discordJoinButton; private static Image? _discordLogoImage; private static Sprite? _ownedMenuLogoSprite; private static Sprite? _ownedDiscordLogoSprite; private static TMP_FontAsset? _cachedDiscordFont; private static string? _pluginDefaultsRoot; private static string? _loadingArtSourceFolder; private static bool _loadingArtSourceResolved; private static readonly string[] ImageExtensions = new string[3] { ".png", ".jpg", ".jpeg" }; private static readonly List<Button> ButtonScratch = new List<Button>(); internal static CustomMainMenuPlugin? Instance { get; private set; } internal static ConfigEntry<bool>? ModEnabled { get; private set; } internal static ManualLogSource? ModLog { get; private set; } internal static bool IsActive { get { CustomMainMenuPlugin instance = Instance; if ((Object)(object)instance != (Object)null && !instance._inactive && ModEnabled != null) { return ModEnabled.Value; } return false; } } internal static GameObject? DiscordPanelRoot => _discordPanel; internal static bool IsVerboseLogging { get { if (_enableLogging != null) { return _enableLogging.Value; } return false; } } private void Awake() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Invalid comparison between Unknown and I4 //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Expected O, but got Unknown Instance = this; ModLog = ((BaseUnityPlugin)this).Logger; if ((int)SystemInfo.graphicsDeviceType == 4) { _inactive = true; ((BaseUnityPlugin)this).Logger.LogInfo((object)"Custom Main Menu is client-only and stays inactive on headless instances."); return; } bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet; ((BaseUnityPlugin)this).Config.SaveOnConfigSet = false; ModEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Turn this mod on or off. Takes effect after restarting the game."); _enableLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableLogging", false, "Write extra log lines (menu lifecycle, loading art, Discord panel). Off by default."); SkipStartupIntro.BindConfig(((BaseUnityPlugin)this).Config); MenuAmbientMute.BindConfig(((BaseUnityPlugin)this).Config); LoadingTips.BindConfig(((BaseUnityPlugin)this).Config); LogoPlacement.BindConfig(((BaseUnityPlugin)this).Config); MenuChangelog.BindConfig(((BaseUnityPlugin)this).Config); DiscordPanelVisibility.BindConfig(((BaseUnityPlugin)this).Config); ConfigLayout.TryWrite(((BaseUnityPlugin)this).Config); ((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet; ModEnabled.SettingChanged += OnEnabledChanged; if (!ModEnabled.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"Custom Main Menu is disabled in config."); return; } EnsureAssetDirectories(); CachePluginDefaultsRoot(); TryInstallHowToCustomize(); ResolveLoadingArtSourceFolderOnce(); MenuChangelog.Initialize(); _harmony = new Harmony("rdmods.custommainmenu"); ApplyHarmonyPatchesSafely(_harmony); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Custom Main Menu 1.5.0 loaded. Asset root: " + GetAssetRootPath())); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Loading art folder: " + GetResolvedLoadingArtFolder())); ((BaseUnityPlugin)this).Logger.LogInfo((object)SkipStartupIntro.DescribeConfig()); ((BaseUnityPlugin)this).Logger.LogInfo((object)LoadingTips.DescribeConfig()); ((BaseUnityPlugin)this).Logger.LogInfo((object)LogoPlacement.DescribeConfig()); } private void OnDestroy() { if (_inactive) { Instance = null; return; } if (ModEnabled != null) { ModEnabled.SettingChanged -= OnEnabledChanged; } SkipStartupIntro.ResetSession(); MenuAmbientMute.Shutdown(); LogoPlacement.ResetMenuSession(); LoadingScreens.StopAllWatchers("plugin destroyed"); ReleaseMenu(); MenuMusic.StopImmediate(); Harmony? harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } _harmony = null; Instance = null; } private void OnEnabledChanged(object sender, EventArgs e) { if (ModEnabled != null && !ModEnabled.Value) { LoadingScreens.StopAllWatchers("mod disabled"); ReleaseMenu(); MenuMusic.StopImmediate(); } } private void ReleaseMenu() { StopRoutine(ref _atmosphereRoutine); StopRoutine(ref _discordLogoRoutine); DestroyDiscordPanel(); ReleaseOwnedMenuLogoSprite(); MainMenuActive = false; WorldMusicMute.Release(); MenuAmbientMute.Restore(); MenuChangelog.StopWatching(); } private void StopRoutine(ref Coroutine? routine) { if (routine != null) { ((MonoBehaviour)this).StopCoroutine(routine); routine = null; } } internal static void LogInfo(string message) { if (IsVerboseLogging) { ManualLogSource? modLog = ModLog; if (modLog != null) { modLog.LogInfo((object)message); } } } internal static void LogWarn(string message) { ManualLogSource? modLog = ModLog; if (modLog != null) { modLog.LogWarning((object)message); } } private void ApplyHarmonyPatchesSafely(Harmony harmony) { int num = 0; int num2 = 0; Type[] typesFromAssembly = AccessTools.GetTypesFromAssembly(typeof(CustomMainMenuPlugin).Assembly); foreach (Type type in typesFromAssembly) { num++; try { harmony.CreateClassProcessor(type).Patch(); } catch (Exception ex) { num2++; ((BaseUnityPlugin)this).Logger.LogError((object)("Harmony patch failed for " + type.FullName + ": " + ex.Message)); } } ((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("{0} Harmony apply finished (types={1}, failures={2}).", "Custom Main Menu", num, num2)); } internal static string GetAssetRootPath() { return Path.Combine(Paths.ConfigPath, "CustomMainMenu"); } internal static string GetLoadingArtFolder() { return Path.Combine(GetAssetRootPath(), "LoadingArt"); } internal static string GetPluginDefaultsRoot() { if (_pluginDefaultsRoot != null) { return _pluginDefaultsRoot; } CachePluginDefaultsRoot(); return _pluginDefaultsRoot ?? string.Empty; } internal static string GetResolvedLoadingArtFolder() { if (!_loadingArtSourceResolved) { ResolveLoadingArtSourceFolderOnce(); } return _loadingArtSourceFolder ?? GetLoadingArtFolder(); } internal static string GetLogoPath() { return ResolveUserOrDefaultImage("logo"); } internal static string GetDiscordLogoPath() { return ResolveUserOrDefaultImage("discordlogo"); } private static string ResolveImagePath(string folder, string baseName) { for (int i = 0; i < ImageExtensions.Length; i++) { string text = Path.Combine(folder, baseName + ImageExtensions[i]); if (File.Exists(text)) { return text; } } return Path.Combine(folder, baseName + ImageExtensions[0]); } private static string ResolveUserOrDefaultImage(string baseName) { string text = ResolveImagePath(GetAssetRootPath(), baseName); if (File.Exists(text)) { return text; } string pluginDefaultsRoot = GetPluginDefaultsRoot(); if (string.IsNullOrEmpty(pluginDefaultsRoot)) { return text; } string text2 = ResolveImagePath(pluginDefaultsRoot, baseName); if (!File.Exists(text2)) { return text; } return text2; } private static string ResolveUserOrDefaultExact(string fileName) { string text = Path.Combine(GetAssetRootPath(), fileName); if (File.Exists(text)) { return text; } string pluginDefaultsRoot = GetPluginDefaultsRoot(); if (string.IsNullOrEmpty(pluginDefaultsRoot)) { return text; } string text2 = Path.Combine(pluginDefaultsRoot, fileName); if (!File.Exists(text2)) { return text; } return text2; } internal static string GetMusicPath() { return ResolveUserOrDefaultExact("mainmenu.ogg"); } internal static string GetChangelogPath() { return ResolveUserOrDefaultExact("changelog.txt"); } internal static string GetCustomTipsPath() { return ResolveUserOrDefaultExact("customtips.txt"); } internal static string? GetRandomLoadingArtPath() { string resolvedLoadingArtFolder = GetResolvedLoadingArtFolder(); if (!Directory.Exists(resolvedLoadingArtFolder)) { return null; } string result = null; int num = 0; for (int i = 1; i <= 12; i++) { string text = ResolveImagePath(resolvedLoadingArtFolder, "art" + i); if (File.Exists(text)) { num++; if (num == 1 || Random.Range(0, num) == 0) { result = text; } } } return result; } private void EnsureAssetDirectories() { try { Directory.CreateDirectory(GetAssetRootPath()); Directory.CreateDirectory(GetLoadingArtFolder()); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not create asset folders: " + ex.Message)); } } private static void TryInstallHowToCustomize() { try { string pluginDefaultsRoot = GetPluginDefaultsRoot(); if (string.IsNullOrEmpty(pluginDefaultsRoot)) { return; } string text = Path.Combine(pluginDefaultsRoot, "README-HOW-TO-CUSTOMIZE.txt"); if (File.Exists(text)) { string assetRootPath = GetAssetRootPath(); Directory.CreateDirectory(assetRootPath); string text2 = Path.Combine(assetRootPath, "README-HOW-TO-CUSTOMIZE.txt"); if (!File.Exists(text2) || !FilesAreIdentical(text, text2)) { File.Copy(text, text2, overwrite: true); } } } catch (Exception ex) { LogWarn("How-to customize file could not be copied: " + ex.Message); } } private static bool FilesAreIdentical(string left, string right) { byte[] array = File.ReadAllBytes(left); byte[] array2 = File.ReadAllBytes(right); if (array.Length != array2.Length) { return false; } for (int i = 0; i < array.Length; i++) { if (array[i] != array2[i]) { return false; } } return true; } private static void CachePluginDefaultsRoot() { try { CustomMainMenuPlugin instance = Instance; string text = (((Object)(object)instance != (Object)null) ? ((BaseUnityPlugin)instance).Info.Location : null); if (string.IsNullOrEmpty(text)) { _pluginDefaultsRoot = string.Empty; return; } string directoryName = Path.GetDirectoryName(text); _pluginDefaultsRoot = (string.IsNullOrEmpty(directoryName) ? string.Empty : Path.Combine(directoryName, "defaults")); } catch { _pluginDefaultsRoot = string.Empty; } } private static void ResolveLoadingArtSourceFolderOnce() { if (!_loadingArtSourceResolved) { _loadingArtSourceResolved = true; string loadingArtFolder = GetLoadingArtFolder(); if (DirectoryHasLoadingImages(loadingArtFolder)) { _loadingArtSourceFolder = loadingArtFolder; return; } string pluginDefaultsRoot = GetPluginDefaultsRoot(); string text = (string.IsNullOrEmpty(pluginDefaultsRoot) ? string.Empty : Path.Combine(pluginDefaultsRoot, "LoadingArt")); _loadingArtSourceFolder = (DirectoryHasLoadingImages(text) ? text : loadingArtFolder); } } private static bool DirectoryHasLoadingImages(string dir) { if (string.IsNullOrEmpty(dir) || !Directory.Exists(dir)) { return false; } try { for (int i = 0; i < ImageExtensions.Length; i++) { using IEnumerator<string> enumerator = Directory.EnumerateFiles(dir, "*" + ImageExtensions[i]).GetEnumerator(); if (enumerator.MoveNext()) { _ = enumerator.Current; return true; } } } catch { return false; } return false; } internal static void OnMainMenuOpened(FejdStartup fejd) { MainMenuActive = true; DiscordPanelVisibility.ResetSessionState(); MenuMusic.RefreshTrackState(); WorldMusicMute.MuteForMenu(); MenuAmbientMute.Apply(); CustomMainMenuPlugin instance = Instance; if ((Object)(object)instance != (Object)null) { instance.StopRoutine(ref instance._atmosphereRoutine); instance._atmosphereRoutine = ((MonoBehaviour)instance).StartCoroutine(MenuAtmosphere.ApplyWhenReady()); } EnsureDiscordPanel(fejd, "main menu opened"); } internal static void OnLogoutToMenu() { MenuMusic.RefreshTrackState(); WorldMusicMute.MuteForMenu(); } internal static void OnWorldLoadCommitted() { LoadingScreens.StopAllWatchers("world load committed"); LogoPlacement.ResetMenuSession(); CustomMainMenuPlugin instance = Instance; if ((Object)(object)instance != (Object)null) { instance.ReleaseMenu(); } LogInfo("Menu released (world load)."); } internal static void PresentBrandedMenu(FejdStartup fejd, string reason) { LogInfo("Main menu presented (" + reason + ")."); OnMainMenuOpened(fejd); MenuChangelog.OnMenuPresented(); ApplyMenuChrome(fejd); MenuChangelog.ApplyOnMenuOpen(fejd); MenuMusic.Start(); } internal static void HideTitleLogo(FejdStartup fejd) { if (!((Object)(object)fejd == (Object)null)) { Transform val = FindLogoRoot(fejd); if ((Object)(object)val != (Object)null && ((Component)val).gameObject.activeSelf) { ((Component)val).gameObject.SetActive(false); } } } internal static void ApplyMenuChrome(FejdStartup fejd) { if (!((Object)(object)fejd == (Object)null)) { HideSerializedClutter(fejd); if (!ApplyCustomLogo(fejd) && !((Object)(object)Instance == (Object)null)) { ((MonoBehaviour)Instance).StartCoroutine(RetryLogoNextFrame(fejd)); } } } private static IEnumerator RetryLogoNextFrame(FejdStartup fejd) { yield return null; if (MainMenuActive && (Object)(object)fejd != (Object)null && !CinematicsManager.IsStartedPlaying()) { ApplyCustomLogo(fejd); } } private static void HideSerializedClutter(FejdStartup fejd) { HideGo(fejd.m_moddedText, "m_moddedText"); HideGo(fejd.m_merchStoreButtonParent, "m_merchStoreButtonParent"); if (!MenuChangelog.ShouldShowPanel()) { HideGo(fejd.m_changeLog, "m_changeLog"); HideGo(fejd.m_showChangelogButton, "m_showChangelogButton"); HideGo(fejd.m_changeLogNotice, "m_changeLogNotice"); } } private static void HideGo(GameObject? go, string label) { if (!((Object)(object)go == (Object)null) && go.activeSelf) { go.SetActive(false); if (IsVerboseLogging) { LogInfo("Hidden " + label + " (" + ((Object)go).name + ")."); } } } private static bool NameHas(string? name, string token) { if (name != null) { return name.IndexOf(token, StringComparison.OrdinalIgnoreCase) >= 0; } return false; } private static Transform? FindLogoRoot(FejdStartup fejd) { if ((Object)(object)fejd.m_mainMenu != (Object)null) { Transform val = FindDeepNorthLogoRoot(fejd.m_mainMenu.transform); if ((Object)(object)val != (Object)null) { return val; } } return FindDeepNorthLogoRoot(((Component)fejd).transform); } private static Transform? FindDeepNorthLogoRoot(Transform root) { string name = ((Object)root).name; if (NameHas(name, "logo") && NameHas(name, "deepnorth")) { return root; } for (int i = 0; i < root.childCount; i++) { Transform val = FindDeepNorthLogoRoot(root.GetChild(i)); if ((Object)(object)val != (Object)null) { return val; } } return null; } private static bool ApplyCustomLogo(FejdStartup fejd) { Transform val = FindLogoRoot(fejd); if ((Object)(object)val != (Object)null) { HideLogoVfxChildren(val); if (!((Component)val).gameObject.activeSelf) { ((Component)val).gameObject.SetActive(true); } } string logoPath = GetLogoPath(); if (!File.Exists(logoPath)) { LogoPlacement.ApplyMenuOffset(((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<Image>() : null); return true; } Image best = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<Image>() : null); int hi = (((Object)(object)best != (Object)null) ? ScoreLogo(best) : int.MinValue); if ((Object)(object)best == (Object)null || hi < 50) { FindBestLogoImage(((Component)fejd).transform, ref best, ref hi); } bool flag = (Object)(object)best != (Object)null && LogoLooksNamed(best); if ((Object)(object)best == (Object)null || (hi < 50 && !(hi >= 25 && flag))) { if (IsVerboseLogging) { LogWarn($"Logo not applied (best score {hi})."); } return (Object)(object)best != (Object)null; } Sprite val2 = LoadSpriteFromDisk(logoPath); if ((Object)(object)val2 == (Object)null) { return false; } ApplyLogo(best, val2); if (!((Component)best).gameObject.activeSelf) { ((Component)best).gameObject.SetActive(true); } HideLogoVfxChildren(((Component)best).transform); ReplaceOwnedSprite(ref _ownedMenuLogoSprite, val2); LogoPlacement.ApplyMenuOffset(best); if (IsVerboseLogging) { LogInfo($"Logo applied (score {hi}, name={((Object)((Component)best).gameObject).name})."); } return true; } private static void FindBestLogoImage(Transform root, ref Image? best, ref int hi) { Image component = ((Component)root).GetComponent<Image>(); if ((Object)(object)component != (Object)null) { string name = ((Object)root).name; string name2 = (((Object)(object)component.sprite != (Object)null) ? ((Object)component.sprite).name : string.Empty); if (NameHas(name, "logo") || NameHas(name, "valheim") || NameHas(name, "title") || NameHas(name2, "logo") || NameHas(name2, "valheim")) { int num = ScoreLogo(component); if (num > hi) { hi = num; best = component; } } } for (int i = 0; i < root.childCount; i++) { FindBestLogoImage(root.GetChild(i), ref best, ref hi); } } private static bool LogoLooksNamed(Image img) { string name = ((Object)((Component)img).gameObject).name; string name2 = (((Object)(object)img.sprite != (Object)null) ? ((Object)img.sprite).name : string.Empty); if (!NameHas(name, "logo") && !NameHas(name, "valheim") && !NameHas(name, "title") && !NameHas(name2, "logo")) { return NameHas(name2, "valheim"); } return true; } private static int ScoreLogo(Image img) { //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) string name = ((Object)((Component)img).gameObject).name; string name2 = (((Object)(object)img.sprite != (Object)null) ? ((Object)img.sprite).name : string.Empty); int num = 0; if (NameHas(name, "logo")) { num += 100; } if (NameHas(name, "deepnorth")) { num += 40; } if (NameHas(name, "valheim") || NameHas(name2, "valheim")) { num += 80; } if (NameHas(name, "title")) { num += 40; } if (NameHas(name2, "logo")) { num += 50; } if (((Component)img).gameObject.activeInHierarchy) { num += 15; } if (((Behaviour)img).enabled) { num += 10; } RectTransform rectTransform = ((Graphic)img).rectTransform; if ((Object)(object)rectTransform != (Object)null) { Rect rect = rectTransform.rect; float width = ((Rect)(ref rect)).width; rect = rectTransform.rect; if (Mathf.Abs(width * ((Rect)(ref rect)).height) > 10000f) { num += 10; } } return num; } internal static Sprite? LoadSpriteFromDisk(string path) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_0046: 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) try { byte[] data = File.ReadAllBytes(path); Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false); if (!TextureDecode.LoadImage(val, data, markNonReadable: false)) { Object.Destroy((Object)(object)val); return null; } val.Apply(true, false); return Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), 100f); } catch (Exception ex) { LogWarn("logo load: " + ex.Message); return null; } } internal static void DestroyOwnedSprite(Sprite? sprite) { if (!((Object)(object)sprite == (Object)null)) { Texture2D texture = sprite.texture; Object.Destroy((Object)(object)sprite); if ((Object)(object)texture != (Object)null) { Object.Destroy((Object)(object)texture); } } } private static void ReplaceOwnedSprite(ref Sprite? owned, Sprite? next) { if (owned == next) { owned = next; return; } Sprite? sprite = owned; owned = next; DestroyOwnedSprite(sprite); } private static void ReleaseOwnedMenuLogoSprite() { DestroyOwnedSprite(_ownedMenuLogoSprite); _ownedMenuLogoSprite = null; } private static void ReleaseOwnedDiscordLogoSprite() { DestroyOwnedSprite(_ownedDiscordLogoSprite); _ownedDiscordLogoSprite = null; } internal static void HideLogoVfxChildren(Transform logoRoot) { for (int i = 0; i < logoRoot.childCount; i++) { HideLogoVfxWalk(logoRoot.GetChild(i)); } } private static void HideLogoVfxWalk(Transform child) { string name = ((Object)child).name; if ((NameHas(name, "snow") || NameHas(name, "ember") || NameHas(name, "particle")) && ((Component)child).gameObject.activeSelf) { ((Component)child).gameObject.SetActive(false); if (IsVerboseLogging) { LogInfo("Deep North logo VFX hidden (" + ((Object)child).name + ")."); } } for (int i = 0; i < child.childCount; i++) { HideLogoVfxWalk(child.GetChild(i)); } } internal static void ApplyLogo(Image target, Sprite sp) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0058: 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_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) target.sprite = sp; target.overrideSprite = sp; ((Behaviour)target).enabled = true; target.preserveAspect = true; target.type = (Type)0; ((Graphic)target).material = null; ((Graphic)target).color = Color.white; if ((Object)(object)((Graphic)target).rectTransform != (Object)null) { RectTransform rectTransform = ((Graphic)target).rectTransform; Rect rect = sp.rect; float width = ((Rect)(ref rect)).width; rect = sp.rect; rectTransform.sizeDelta = new Vector2(width, ((Rect)(ref rect)).height); ((Transform)((Graphic)target).rectTransform).localScale = Vector3.one; } } private static bool IsUsableTmpFont(TMP_FontAsset? font) { if ((Object)(object)font == (Object)null) { return false; } return !NameHas(((Object)font).name, "LiberationSans"); } private static void ApplyMenuFont(TextMeshProUGUI target, TextMeshProUGUI? template) { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) TMP_FontAsset val = null; if ((Object)(object)template != (Object)null && IsUsableTmpFont(((TMP_Text)template).font)) { val = ((TMP_Text)template).font; ((TMP_Text)target).fontSharedMaterial = ((TMP_Text)template).fontSharedMaterial; ((TMP_Text)target).fontSize = Mathf.Clamp(((TMP_Text)template).fontSize - 1f, 16f, 28f); ((Graphic)target).color = ((Graphic)template).color; } if ((Object)(object)val == (Object)null) { val = FindAnyUsableTmpFont(); ((TMP_Text)target).fontSize = 22f; if ((Object)(object)template == (Object)null || !IsUsableTmpFont(((TMP_Text)template).font)) { ((Graphic)target).color = new Color(0.93f, 0.79f, 0.42f); } } if ((Object)(object)val != (Object)null) { ((TMP_Text)target).font = val; } else { LogWarn("Discord title: no usable TMP font found."); } } private static TMP_FontAsset? FindAnyUsableTmpFont() { if (IsUsableTmpFont(_cachedDiscordFont)) { return _cachedDiscordFont; } TMP_FontAsset val = null; FejdStartup instance = FejdStartup.instance; if ((Object)(object)instance != (Object)null) { TextMeshProUGUI[] componentsInChildren = ((Component)instance).GetComponentsInChildren<TextMeshProUGUI>(true); foreach (TextMeshProUGUI val2 in componentsInChildren) { if ((Object)(object)val2 != (Object)null && IsUsableTmpFont(((TMP_Text)val2).font)) { val = ((TMP_Text)val2).font; break; } } } if ((Object)(object)val == (Object)null) { TMP_FontAsset defaultFontAsset = TMP_Settings.defaultFontAsset; val = (IsUsableTmpFont(defaultFontAsset) ? defaultFontAsset : null); } _cachedDiscordFont = val; return val; } internal static void EnsureDiscordPanel(FejdStartup fejd, string reason) { if (!((Object)(object)fejd == (Object)null)) { if ((Object)(object)_discordPanel == (Object)null) { CreateDiscordPanel(fejd); } else { DiscordPanelVisibility.Refresh(fejd, reason); } } } internal static void DestroyDiscordPanel() { CustomMainMenuPlugin instance = Instance; if ((Object)(object)instance != (Object)null) { instance.StopRoutine(ref instance._discordLogoRoutine); } if ((Object)(object)_discordPanel != (Object)null) { Object.Destroy((Object)(object)_discordPanel); } _discordPanel = null; _discordJoinButton = null; _discordLogoImage = null; ReleaseOwnedDiscordLogoSprite(); DiscordPanelVisibility.ResetSessionState(); } private static void EnsureParentedToStartMenuCanvas(FejdStartup fejd) { if ((Object)(object)_discordPanel == (Object)null) { return; } Canvas val = DiscordPanelVisibility.ResolveStartMenuCanvas(fejd); if (!((Object)(object)val == (Object)null) && !((Object)(object)_discordPanel.transform.parent == (Object)(object)((Component)val).transform)) { _discordPanel.transform.SetParent(((Component)val).transform, false); ApplyDiscordPanelAnchors(_discordPanel.GetComponent<RectTransform>()); if (IsVerboseLogging) { LogInfo("Discord panel reparented to start-menu canvas (" + ((Object)val).name + ")."); } } } private static void ApplyDiscordPanelAnchors(RectTransform? rootRt) { //IL_0015: 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_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: 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_0074: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)rootRt == (Object)null)) { rootRt.anchorMin = new Vector2(1f, 0f); rootRt.anchorMax = new Vector2(1f, 0f); rootRt.pivot = new Vector2(1f, 0f); rootRt.anchoredPosition = new Vector2(-36f, 105f); rootRt.sizeDelta = new Vector2(328f, 360f); ((Transform)rootRt).localScale = Vector3.one; } } internal static Button? FindStartMenuButton(FejdStartup startup) { if ((Object)(object)startup == (Object)null) { return null; } GameObject val = (((Object)(object)startup.m_menuList != (Object)null) ? startup.m_menuList : startup.m_mainMenu); if ((Object)(object)val == (Object)null) { return null; } val.GetComponentsInChildren<Button>(true, ButtonScratch); Button val2 = null; Button val3 = null; for (int i = 0; i < ButtonScratch.Count; i++) { Button val4 = ButtonScratch[i]; if (!((Object)(object)val4 == (Object)null) && !IsOurDiscordUi(((Component)val4).gameObject)) { if (val2 == null) { val2 = val4; } if (NameHas(((Object)((Component)val4).gameObject).name, "start")) { val3 = val4; break; } } } ButtonScratch.Clear(); return val3 ?? val2; } private static bool IsOurDiscordUi(GameObject go) { Transform val = go.transform; while ((Object)(object)val != (Object)null) { if (((Object)val).name == "CustomMainMenuDiscordPanel") { return true; } val = val.parent; } return false; } internal static void SetButtonText(GameObject go, string txt) { TextMeshProUGUI[] componentsInChildren = go.GetComponentsInChildren<TextMeshProUGUI>(true); foreach (TextMeshProUGUI obj in componentsInChildren) { ((TMP_Text)obj).richText = false; ((TMP_Text)obj).text = txt; } Text[] componentsInChildren2 = go.GetComponentsInChildren<Text>(true); foreach (Text obj2 in componentsInChildren2) { obj2.supportRichText = false; obj2.text = txt; } } private static void NormalizeRectForLayoutGroup(RectTransform rt) { //IL_0001: 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_0040: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) ((Transform)rt).localScale = Vector3.one; rt.anchorMin = new Vector2(0f, 0f); rt.anchorMax = new Vector2(1f, 1f); rt.pivot = new Vector2(0.5f, 0.5f); rt.anchoredPosition = Vector2.zero; rt.offsetMin = Vector2.zero; rt.offsetMax = Vector2.zero; } internal static void CreateDiscordPanel(FejdStartup? fejd = null) { //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Expected O, but got Unknown //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_01af: Unknown result type (might be due to invalid IL or missing references) //IL_01d8: Unknown result type (might be due to invalid IL or missing references) //IL_01de: Expected O, but got Unknown //IL_022a: Unknown result type (might be due to invalid IL or missing references) //IL_025a: Unknown result type (might be due to invalid IL or missing references) //IL_025f: Unknown result type (might be due to invalid IL or missing references) //IL_0271: Unknown result type (might be due to invalid IL or missing references) //IL_0278: Unknown result type (might be due to invalid IL or missing references) //IL_0283: Unknown result type (might be due to invalid IL or missing references) //IL_028e: Unknown result type (might be due to invalid IL or missing references) //IL_0293: Unknown result type (might be due to invalid IL or missing references) //IL_0294: Unknown result type (might be due to invalid IL or missing references) //IL_029b: Unknown result type (might be due to invalid IL or missing references) //IL_02d8: Unknown result type (might be due to invalid IL or missing references) try { if (fejd == null) { fejd = FejdStartup.instance; } if ((Object)(object)_discordPanel != (Object)null) { if ((Object)(object)fejd != (Object)null) { EnsureParentedToStartMenuCanvas(fejd); EnsureDiscordJoinButton(fejd, null); DiscordPanelVisibility.Refresh(fejd, "Discord panel reuse"); } } else { if ((Object)(object)fejd == (Object)null || (Object)(object)Instance == (Object)null) { return; } Canvas val = DiscordPanelVisibility.ResolveStartMenuCanvas(fejd); if ((Object)(object)val == (Object)null) { LogInfo("Discord panel deferred: start-menu canvas not ready."); return; } _discordPanel = new GameObject("CustomMainMenuDiscordPanel"); _discordPanel.transform.SetParent(((Component)val).transform, false); ApplyDiscordPanelAnchors(_discordPanel.AddComponent<RectTransform>()); VerticalLayoutGroup obj = _discordPanel.AddComponent<VerticalLayoutGroup>(); ((LayoutGroup)obj).childAlignment = (TextAnchor)4; ((HorizontalOrVerticalLayoutGroup)obj).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)obj).childControlHeight = false; ((HorizontalOrVerticalLayoutGroup)obj).childForceExpandWidth = true; ((HorizontalOrVerticalLayoutGroup)obj).spacing = 16f; _discordPanel.AddComponent<ContentSizeFitter>().verticalFit = (FitMode)2; Button val2 = FindStartMenuButton(fejd); TextMeshProUGUI template = (((Object)(object)val2 != (Object)null) ? ((Component)val2).GetComponentInChildren<TextMeshProUGUI>(true) : null); GameObject val3 = new GameObject("DiscordTitle", new Type[1] { typeof(RectTransform) }); val3.SetActive(false); val3.transform.SetParent(_discordPanel.transform, false); NormalizeRectForLayoutGroup(val3.GetComponent<RectTransform>()); LayoutElement obj2 = val3.AddComponent<LayoutElement>(); obj2.flexibleWidth = 1f; obj2.preferredWidth = 300f; obj2.preferredHeight = 90f; TextMeshProUGUI obj3 = val3.AddComponent<TextMeshProUGUI>(); ((TMP_Text)obj3).richText = false; ((TMP_Text)obj3).text = "Need help? Join Discord!"; ((TMP_Text)obj3).horizontalAlignment = (HorizontalAlignmentOptions)2; ((TMP_Text)obj3).verticalAlignment = (VerticalAlignmentOptions)512; ((TMP_Text)obj3).textWrappingMode = (TextWrappingModes)1; ((TMP_Text)obj3).overflowMode = (TextOverflowModes)0; ((TMP_Text)obj3).margin = Vector4.zero; ((TMP_Text)obj3).enableAutoSizing = false; ((Graphic)obj3).raycastTarget = false; ApplyMenuFont(obj3, template); val3.SetActive(true); GameObject val4 = new GameObject("DiscordLogo"); val4.transform.SetParent(_discordPanel.transform, false); LayoutElement obj4 = val4.AddComponent<LayoutElement>(); obj4.preferredHeight = 150f; obj4.preferredWidth = 300f; _discordLogoImage = val4.AddComponent<Image>(); _discordLogoImage.type = (Type)0; ((Graphic)_discordLogoImage).color = Color.white; ((Graphic)_discordLogoImage).material = null; _discordLogoImage.preserveAspect = true; ((Graphic)_discordLogoImage).raycastTarget = false; GameObject val5 = new GameObject("DiscordGlyph"); val5.transform.SetParent(val4.transform, false); RectTransform obj5 = val5.AddComponent<RectTransform>(); obj5.anchorMin = Vector2.zero; obj5.anchorMax = Vector2.one; Vector2 offsetMin = (obj5.offsetMax = Vector2.zero); obj5.offsetMin = offsetMin; Text obj6 = val5.AddComponent<Text>(); obj6.text = "DIS"; obj6.font = Resources.GetBuiltinResource<Font>("Arial.ttf"); obj6.fontSize = 24; obj6.fontStyle = (FontStyle)1; obj6.alignment = (TextAnchor)4; ((Graphic)obj6).color = Color.white; EnsureDiscordJoinButton(fejd, val2); Instance._discordLogoRoutine = ((MonoBehaviour)Instance).StartCoroutine(LoadDiscordLogoDelayed()); DiscordPanelVisibility.Refresh(fejd, "Discord panel created"); if (IsVerboseLogging) { LogInfo("Discord panel ready (canvas=" + ((Object)val).name + ")."); } } } catch (Exception ex) { ManualLogSource? modLog = ModLog; if (modLog != null) { modLog.LogError((object)("Discord panel failed: " + ex)); } } } private static void EnsureDiscordJoinButton(FejdStartup fejd, Button? startButton, float discordContentWidth = 300f) { //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Expected O, but got Unknown //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Expected O, but got Unknown if ((Object)(object)_discordPanel == (Object)null) { return; } if ((Object)(object)_discordJoinButton != (Object)null) { _discordJoinButton.SetActive(true); return; } Button val = (((Object)(object)startButton != (Object)null) ? startButton : FindStartMenuButton(fejd)); if ((Object)(object)val != (Object)null) { GameObject val2 = Object.Instantiate<GameObject>(((Component)val).gameObject, _discordPanel.transform, false); val2.SetActive(true); LayoutElement obj = val2.GetComponent<LayoutElement>() ?? val2.AddComponent<LayoutElement>(); obj.preferredWidth = discordContentWidth; obj.preferredHeight = 46f; Button component = val2.GetComponent<Button>(); if ((Object)(object)component != (Object)null) { component.onClick = new ButtonClickedEvent(); ButtonClickedEvent onClick = component.onClick; object obj2 = <>O.<0>__OnDiscordJoinClicked; if (obj2 == null) { UnityAction val3 = OnDiscordJoinClicked; <>O.<0>__OnDiscordJoinClicked = val3; obj2 = (object)val3; } ((UnityEvent)onClick).AddListener((UnityAction)obj2); SetButtonText(val2, "Click here to join Discord!"); } _discordJoinButton = val2; } else { _discordJoinButton = CreateFallbackDiscordButton(_discordPanel.transform, discordContentWidth); } } private static GameObject CreateFallbackDiscordButton(Transform parent, float width) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Expected O, but got Unknown //IL_0090: 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_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: 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_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Expected O, but got Unknown GameObject val = new GameObject("DiscordJoinButton", new Type[5] { typeof(RectTransform), typeof(CanvasRenderer), typeof(Image), typeof(Button), typeof(LayoutElement) }); val.transform.SetParent(parent, false); LayoutElement component = val.GetComponent<LayoutElement>(); component.preferredWidth = width; component.preferredHeight = 46f; ((Graphic)val.GetComponent<Image>()).color = new Color(0.18f, 0.14f, 0.08f, 0.92f); ButtonClickedEvent onClick = val.GetComponent<Button>().onClick; object obj = <>O.<0>__OnDiscordJoinClicked; if (obj == null) { UnityAction val2 = OnDiscordJoinClicked; <>O.<0>__OnDiscordJoinClicked = val2; obj = (object)val2; } ((UnityEvent)onClick).AddListener((UnityAction)obj); GameObject val3 = new GameObject("Text", new Type[1] { typeof(RectTransform) }); val3.transform.SetParent(val.transform, false); NormalizeRectForLayoutGroup(val3.GetComponent<RectTransform>()); TextMeshProUGUI obj2 = val3.AddComponent<TextMeshProUGUI>(); ((TMP_Text)obj2).richText = false; ((TMP_Text)obj2).text = "Click here to join Discord!"; ((TMP_Text)obj2).horizontalAlignment = (HorizontalAlignmentOptions)2; ((TMP_Text)obj2).verticalAlignment = (VerticalAlignmentOptions)512; ((TMP_Text)obj2).enableAutoSizing = false; ((Graphic)obj2).raycastTarget = false; ApplyMenuFont(obj2, null); return val; } private static void OnDiscordJoinClicked() { if (!DiscordPanelVisibility.TryOpenInviteUrl()) { LogWarn("Discord invite URL is missing or invalid in config."); } } private static IEnumerator LoadDiscordLogoDelayed() { yield return null; CustomMainMenuPlugin instance = Instance; if ((Object)(object)instance != (Object)null) { instance._discordLogoRoutine = null; } if ((Object)(object)_discordLogoImage == (Object)null) { yield break; } string discordLogoPath = GetDiscordLogoPath(); if (!File.Exists(discordLogoPath)) { yield break; } Sprite val = LoadSpriteFromDisk(discordLogoPath); if ((Object)(object)val == (Object)null) { yield break; } if ((Object)(object)_discordLogoImage == (Object)null) { DestroyOwnedSprite(val); yield break; } _discordLogoImage.sprite = val; _discordLogoImage.overrideSprite = val; _discordLogoImage.type = (Type)0; ((Graphic)_discordLogoImage).color = Color.white; ((Graphic)_discordLogoImage).material = null; ReplaceOwnedSprite(ref _ownedDiscordLogoSprite, val); foreach (Transform item in ((Component)_discordLogoImage).transform) { Transform val2 = item; if (((Object)val2).name == "DiscordGlyph") { ((Component)val2).gameObject.SetActive(false); } } } } internal static class DiscordPanelVisibility { private static ConfigEntry<bool>? _visibleOnlyOnStartMenu; private static ConfigEntry<string>? _discordInviteUrl; private static bool? _lastAppliedVisible; internal static void BindConfig(ConfigFile config) { _visibleOnlyOnStartMenu = config.Bind<bool>("Discord Panel", "VisibleOnlyOnStartMenu", true, "When true, the Discord panel is visible only on the main Start Game screen. Hidden on character/world select and during load."); _discordInviteUrl = config.Bind<string>("Discord Panel", "DiscordInviteUrl", "https://discord.gg/yX9tv3FXY6", "Discord invite opened by the panel button (http or https). Leave empty to disable the link."); } internal static bool TryOpenInviteUrl() { string text = (_discordInviteUrl?.Value ?? string.Empty).Trim(); if (!IsValidInviteUrl(text)) { return false; } Application.OpenURL(text); CustomMainMenuPlugin.LogInfo("Discord opened."); return true; } private static bool IsValidInviteUrl(string url) { if (string.IsNullOrWhiteSpace(url)) { return false; } if (!Uri.TryCreate(url, UriKind.Absolute, out Uri result)) { return false; } if (!(result.Scheme == Uri.UriSchemeHttp)) { return result.Scheme == Uri.UriSchemeHttps; } return true; } internal static bool IsGatingEnabled() { if (_visibleOnlyOnStartMenu != null) { return _visibleOnlyOnStartMenu.Value; } return true; } internal static void Refresh(FejdStartup? fejd, string reason) { Evaluate(fejd, null, changedActive: false, reason); } internal static void Evaluate(FejdStartup? fejd, RelayKind? changed, bool changedActive, string reason) { GameObject discordPanelRoot = CustomMainMenuPlugin.DiscordPanelRoot; if (!((Object)(object)discordPanelRoot == (Object)null)) { ApplyVisible(discordPanelRoot, ComputeShouldBeVisible(fejd, changed, changedActive), reason); } } private static bool ComputeShouldBeVisible(FejdStartup? fejd, RelayKind? changed, bool changedActive) { if (!CustomMainMenuPlugin.MainMenuActive) { return false; } if (IsOverlayBlockingStartMenu(fejd, changed, changedActive)) { return false; } if (!IsGatingEnabled()) { return true; } if ((Object)(object)fejd == (Object)null || (Object)(object)fejd.m_mainMenu == (Object)null) { return false; } if (changed != RelayKind.Main) { return fejd.m_mainMenu.activeInHierarchy; } return changedActive; } private static bool IsOverlayBlockingStartMenu(FejdStartup? fejd, RelayKind? changed, bool changedActive) { if (CinematicsManager.IsStartedPlaying()) { return true; } if ((Object)(object)fejd == (Object)null) { return false; } GameObject cinematicsMenuList = fejd.m_cinematicsMenuList; if ((Object)(object)cinematicsMenuList != (Object)null && ((changed == RelayKind.Cinematics) ? changedActive : cinematicsMenuList.activeInHierarchy)) { return true; } EulaWindow eulaWindow = fejd.m_eulaWindow; if ((Object)(object)eulaWindow != (Object)null && ((changed == RelayKind.Eula) ? changedActive : eulaWindow.IsWindowActive())) { return true; } return false; } internal static Canvas? ResolveStartMenuCanvas(FejdStartup fejd) { if ((Object)(object)fejd == (Object)null) { return null; } if ((Object)(object)fejd.m_mainMenu != (Object)null) { Canvas componentInParent = fejd.m_mainMenu.GetComponentInParent<Canvas>(true); if ((Object)(object)componentInParent != (Object)null) { return componentInParent; } } Canvas componentInParent2 = ((Component)fejd).GetComponentInParent<Canvas>(true); if ((Object)(object)componentInParent2 != (Object)null) { return componentInParent2; } return ((Component)fejd).GetComponentInChildren<Canvas>(true); } private static void ApplyVisible(GameObject panel, bool visible, string reason) { if (_lastAppliedVisible != visible || panel.activeSelf != visible) { panel.SetActive(visible); _lastAppliedVisible = visible; if (CustomMainMenuPlugin.IsVerboseLogging) { CustomMainMenuPlugin.LogInfo(string.Format("Discord panel {0} ({1}); gating={2}.", visible ? "shown" : "hidden", reason, IsGatingEnabled())); } } } internal static void ResetSessionState() { _lastAppliedVisible = null; } } internal static class LoadingScreens { private const float MaxLoadWaitSeconds = 2400f; private const float LoadingFadeOutSeconds = 0.6f; private static Coroutine? _fadeCoroutine; private static Coroutine? _timeoutCoroutine; private static bool _sessionArmed; private static bool _spawnHookAttached; private static string? _artPath; private static Image? _artImage; private static Sprite? _artSprite; private static Sprite? _artOriginalSprite; private static Color _artOriginalColor; private static bool _chromeApplied; private static GameObject? _hiddenTopBar; private static GameObject? _hiddenLogoGo; private static Image? _logoImage; private static Sprite? _logoOriginalSprite; private static Sprite? _logoOriginalOverride; private static Color _logoOriginalColor; private static bool _logoOriginalEnabled; private static bool _logoOriginalPreserveAspect; private static Vector2 _logoOriginalSizeDelta; private static Vector2 _logoOriginalAnchoredPos; private static Image? _logoOffsetImage; private static bool _logoPosCaptured; private static Sprite? _customLoadingLogoSprite; private static readonly List<Image> _disabledBackdrops = new List<Image>(4); private static readonly Vector3[] _childCorners = (Vector3[])(object)new Vector3[4]; private static readonly Vector3[] _parentCorners = (Vector3[])(object)new Vector3[4]; internal static void ResetForMainMenu() { StopAllWatchers("main menu reset"); } internal static void StopAllWatchers(string reason) { if ((_sessionArmed || (Object)(object)_artImage != (Object)null) && CustomMainMenuPlugin.IsVerboseLogging) { LogLoading("Loading art session ended (" + reason + ")."); } StopCoroutineRef(ref _fadeCoroutine); StopCoroutineRef(ref _timeoutCoroutine); DetachSpawnHook(); RestoreLoadingChrome(); LoadingTips.ResetSession(); ClearArt(); DestroyArtSprite(); _sessionArmed = false; _artPath = null; _chromeApplied = false; } internal static void TryStartLoadingScreenWatcher(string trigger) { CustomMainMenuPlugin instance = CustomMainMenuPlugin.Instance; if (!((Object)(object)instance == (Object)null) && CustomMainMenuPlugin.IsActive && !CustomMainMenuPlugin.MainMenuActive && !_sessionArmed && !((Object)(object)_artImage != (Object)null)) { _artPath = CustomMainMenuPlugin.GetRandomLoadingArtPath(); bool flag = !string.IsNullOrEmpty(_artPath) && File.Exists(_artPath); LoadingTips.ReloadFromDisk(); bool flag2 = LoadingTips.HasCustomTips(); bool flag3 = LoadingTips.HideTopLetterboxEnabled(); bool flag4 = LoadingTips.HideTipBackdropEnabled(); bool flag5 = LoadingTips.UseCustomLoadingLogoEnabled(); _sessionArmed = true; AttachSpawnHook(); _timeoutCoroutine = ((MonoBehaviour)instance).StartCoroutine(TimeoutGuardCoroutine()); if (!flag) { _artPath = null; } if (CustomMainMenuPlugin.IsVerboseLogging) { LogLoading(flag ? ("Loading art armed (" + trigger + "): " + _artPath) : $"Loading overlay armed ({trigger}): tips={flag2}, hideTopLetterbox={flag3}, hideTipBackdrop={flag4}, customLogo={flag5}."); } TryApplyToHud(Hud.instance, trigger); } } internal static void OnHudAwake(Hud hud) { if (_sessionArmed) { TryApplyToHud(hud, "Hud.Awake"); } } private static void TryApplyToHud(Hud? hud, string trigger) { if ((Object)(object)hud == (Object)null) { return; } LoadingTips.CaptureVanillaIfNeeded(hud); TryApplyLoadingChrome(hud, trigger); if (!((Object)(object)_artImage != (Object)null) && !string.IsNullOrEmpty(_artPath) && !((Object)(object)hud.m_loadingImage == (Object)null)) { ApplyArt(_artPath, hud.m_loadingImage); if (CustomMainMenuPlugin.IsVerboseLogging && (Object)(object)_artImage != (Object)null) { LogLoading("Loading art applied via Hud.m_loadingImage (" + trigger + ")."); } } } private static void ApplyArt(string path, Image image) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) Sprite val = null; try { val = SpriteFromFile(path); if (!((Object)(object)val == (Object)null)) { _artOriginalSprite = image.sprite; _artOriginalColor = ((Graphic)image).color; ApplyLoadingImage(image, val); _artImage = image; _artSprite = val; } } catch (Exception ex) { CustomMainMenuPlugin.LogWarn("Hud loading art failed: " + ex.Message); CustomMainMenuPlugin.DestroyOwnedSprite(val); _artImage = null; _artSprite = null; } } private static void TryApplyLoadingChrome(Hud hud, string trigger) { if (_chromeApplied || (Object)(object)hud.m_loadingScreen == (Object)null) { return; } bool flag = LoadingTips.HideTopLetterboxEnabled(); bool flag2 = LoadingTips.HideTipBackdropEnabled(); bool flag3 = LoadingTips.ShouldShowCustomLoadingLogo(); bool flag4 = LoadingTips.UseCustomLoadingLogoEnabled(); if (!flag && !flag2 && !flag4) { return; } Transform transform = ((Component)hud.m_loadingScreen).transform; RectTransform val = (RectTransform)(object)((transform is RectTransform) ? transform : null); if ((Object)(object)val == (Object)null) { _chromeApplied = true; return; } Transform tip = (((Object)(object)hud.m_loadingTip != (Object)null) ? hud.m_loadingTip.transform : null); Transform art = (((Object)(object)hud.m_loadingImage != (Object)null) ? ((Component)hud.m_loadingImage).transform : null); Transform indicator = (((Object)(object)hud.m_loadingIndicator != (Object)null) ? ((Component)hud.m_loadingIndicator).transform : null); Image[] componentsInChildren = ((Component)transform).GetComponentsInChildren<Image>(true); GameObject val2 = null; float num = float.MinValue; Image val3 = null; int num2 = int.MinValue; foreach (Image val4 in componentsInChildren) { if ((Object)(object)val4 == (Object)null || (Object)(object)((Component)val4).gameObject == (Object)null) { continue; } Transform transform2 = ((Component)val4).transform; if (IsArtOrIndicator(transform2, transform, art, indicator)) { continue; } RectTransform rectTransform = ((Graphic)val4).rectTransform; float normalizedCenterY = GetNormalizedCenterY(rectTransform, val); if (LooksLikeLetterboxBar(rectTransform, val)) { if (normalizedCenterY >= 0.62f && !IsTipAncestor(transform2, transform, tip) && normalizedCenterY > num) { num = normalizedCenterY; val2 = ((Component)val4).gameObject; } continue; } int num3 = ScoreLoadingTitleLogo(val4, normalizedCenterY); if (num3 > num2) { num2 = num3; val3 = val4; } } if (flag) { if ((Object)(object)val2 != (Object)null) { val2.SetActive(false); _hiddenTopBar = val2; if (CustomMainMenuPlugin.IsVerboseLogging) { LogLoading("Top loading letterbox hidden (" + trigger + "): " + GetHierarchyPath(val2.transform) + "."); } } else { LogLoading("Top loading letterbox not found (" + trigger + "); vanilla top bar left as-is."); } } if ((Object)(object)val3 != (Object)null && num2 >= 40) { if (flag3) { TryApplyCustomLoadingLogo(val3, trigger); } else if (flag) { ((Component)val3).gameObject.SetActive(false); _hiddenLogoGo = ((Component)val3).gameObject; if (CustomMainMenuPlugin.IsVerboseLogging) { LogLoading("Vanilla loading logo hidden (" + trigger + "): " + GetHierarchyPath(((Component)val3).transform) + "."); } } else { ApplyLoadingLogoOffset(val3); } } else if (flag || flag3) { LogLoading("Loading title logo not found (" + trigger + ")."); } if (flag2) { HideTipBackdrops(componentsInChildren, transform, val, art, indicator, val2, val3, trigger); } _chromeApplied = true; } private static void TryApplyCustomLoadingLogo(Image logo, string trigger) { //IL_007b: 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) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) string logoPath = CustomMainMenuPlugin.GetLogoPath(); if (string.IsNullOrEmpty(logoPath) || !File.Exists(logoPath)) { LogLoading("Custom loading logo skipped (" + trigger + "): logo file missing."); ApplyLoadingLogoOffset(logo); return; } Sprite val = CustomMainMenuPlugin.LoadSpriteFromDisk(logoPath); if ((Object)(object)val == (Object)null) { LogLoading("Custom loading logo skipped (" + trigger + "): decode failed."); ApplyLoadingLogoOffset(logo); return; } _logoImage = logo; _logoOriginalSprite = logo.sprite; _logoOriginalOverride = logo.overrideSprite; _logoOriginalColor = ((Graphic)logo).color; _logoOriginalEnabled = ((Behaviour)logo).enabled; _logoOriginalPreserveAspect = logo.preserveAspect; _logoOriginalSizeDelta = (((Object)(object)((Graphic)logo).rectTransform != (Object)null) ? ((Graphic)logo).rectTransform.sizeDelta : Vector2.zero); _customLoadingLogoSprite = val; CustomMainMenuPlugin.ApplyLogo(logo, val); if (!((Component)logo).gameObject.activeSelf) { ((Component)logo).gameObject.SetActive(true); } CustomMainMenuPlugin.HideLogoVfxChildren(((Component)logo).transform); ApplyLoadingLogoOffset(logo); if (CustomMainMenuPlugin.IsVerboseLogging) { LogLoading("Custom loading logo applied (" + trigger + "): " + GetHierarchyPath(((Component)logo).transform) + "."); } } private static void ApplyLoadingLogoOffset(Image logo) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //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) if (!((Object)(object)logo == (Object)null) && !((Object)(object)((Graphic)logo).rectTransform == (Object)null)) { if (!_logoPosCaptured) { _logoOriginalAnchoredPos = ((Graphic)logo).rectTransform.anchoredPosition; _logoOffsetImage = logo; _logoPosCaptured = true; } LogoPlacement.ApplyLoadingOffset(((Graphic)logo).rectTransform, _logoOriginalAnchoredPos); } } private static void HideTipBackdrops(Image[] images, Transform root, RectTransform rootRt, Transform? art, Transform? indicator, GameObject? topBar, Image? logo, string trigger) { int num = 0; foreach (Image val in images) { if ((Object)(object)val == (Object)null || (Object)(object)((Component)val).gameObject == (Object)null || !((Behaviour)val).enabled) { continue; } Transform transform = ((Component)val).transform; if (IsArtOrIndicator(transform, root, art, indicator) || ((Object)(object)topBar != (Object)null && (Object)(object)transform == (Object)(object)topBar.transform) || ((Object)(object)logo != (Object)null && (Object)(object)transform == (Object)(object)((Component)logo).transform)) { continue; } RectTransform rectTransform = ((Graphic)val).rectTransform; if (LooksLikeLetterboxBar(rectTransform, rootRt) && !(GetNormalizedCenterY(rectTransform, rootRt) > 0.45f)) { ((Behaviour)val).enabled = false; ((Graphic)val).raycastTarget = false; _disabledBackdrops.Add(val); num++; if (CustomMainMenuPlugin.IsVerboseLogging) { LogLoading("Loading tip backdrop hidden (" + trigger + "): " + GetHierarchyPath(transform) + "."); } } } if (num == 0) { LogLoading("Loading tip backdrop not found (" + trigger + "); vanilla bottom bar left as-is."); } } private static void RestoreLoadingChrome() { //IL_004c: 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_00c8: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)_hiddenTopBar)) { _hiddenTopBar.SetActive(true); } _hiddenTopBar = null; if (Object.op_Implicit((Object)(object)_logoImage)) { _logoImage.sprite = _logoOriginalSprite; _logoImage.overrideSprite = _logoOriginalOverride; ((Graphic)_logoImage).color = _logoOriginalColor; ((Behaviour)_logoImage).enabled = _logoOriginalEnabled; _logoImage.preserveAspect = _logoOriginalPreserveAspect; if ((Object)(object)((Graphic)_logoImage).rectTransform != (Object)null) { ((Graphic)_logoImage).rectTransform.sizeDelta = _logoOriginalSizeDelta; } } _logoImage = null; if (Object.op_Implicit((Object)(object)_logoOffsetImage) && (Object)(object)((Graphic)_logoOffsetImage).rectTransform != (Object)null) { ((Graphic)_logoOffsetImage).rectTransform.anchoredPosition = _logoOriginalAnchoredPos; } _logoOffsetImage = null; _logoPosCaptured = false; if (Object.op_Implicit((Object)(object)_hiddenLogoGo)) { _hiddenLogoGo.SetActive(true); } _hiddenLogoGo = null; CustomMainMenuPlugin.DestroyOwnedSprite(_customLoadingLogoSprite); _customLoadingLogoSprite = null; _logoOriginalSprite = null; _logoOriginalOverride = null; for (int i = 0; i < _disabledBackdrops.Count; i++) { Image val = _disabledBackdrops[i]; if ((Object)(object)val != (Object)null) { ((Behaviour)val).enabled = true; } } _disabledBackdrops.Clear(); _chromeApplied = false; } private static bool IsArtOrIndicator(Transform t, Transform root, Transform? art, Transform? indicator) { if ((Object)(object)t == (Object)(object)root) { return true; } if ((Object)(object)art != (Object)null && ((Object)(object)t == (Object)(object)art || t.IsChildOf(art))) { return true; } if ((Object)(object)indicator != (Object)null) { if (!((Object)(object)t == (Object)(object)indicator)) { return t.IsChildOf(indicator); } return true; } return false; } private static bool IsTipAncestor(Transform t, Transform root, Transform? tip) { if ((Object)(object)tip == (Object)null) { return false; } Transform val = tip; while ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)root) { if ((Object)(object)val == (Object)(object)t) { return true; } val = val.parent; } return false; } private static int ScoreLoadingTitleLogo(Image img, float normalizedY) { if (normalizedY < 0.45f) { return int.MinValue; } string text = (((Object)((Component)img).gameObject).name ?? string.Empty).ToLowerInvariant(); string text2 = (((Object)(object)img.sprite != (Object)null) ? ((Object)img.sprite).name.ToLowerInvariant() : string.Empty); if (text.Contains("tip") || text.Contains("progress") || text.Contains("spin") || text.Contains("bar") || text.Contains("letter") || text2.Contains("tip")) { return int.MinValue; } int num = 0; if (text.Contains("logo") || text2.Contains("logo")) { num += 100; } if (text.Contains("valheim") || text2.Contains("valheim")) { num += 80; } if (text.Contains("title")) { num += 40; } if (num == 0) { return int.MinValue; } if (normalizedY >= 0.62f) { num += 20; } return num; } private static bool LooksLikeLetterboxBar(RectTransform rt, RectTransform root) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: 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) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) if (IsFullscreenLike(rt)) { return false; } Rect rect = rt.rect; float num = Mathf.Abs(((Rect)(ref rect)).width); rect = rt.rect; float num2 = Mathf.Abs(((Rect)(ref rect)).height); rect = root.rect; float num3 = Mathf.Abs(((Rect)(ref rect)).width); rect = root.rect; float num4 = Mathf.Abs(((Rect)(ref rect)).height); if (num3 < 8f || num4 < 8f) { return false; } if (num < num3 * 0.78f) { return false; } float num5 = num2 / num4; if (num5 >= 0.04f) { return num5 <= 0.34f; } return false; } private static float GetNormalizedCenterY(RectTransform rt, RectTransform root) { rt.GetWorldCorners(_childCorners); root.GetWorldCorners(_parentCorners); float y = _parentCorners[0].y; float num = _parentCorners[1].y - y; if (Mathf.Abs(num) < 0.001f) { return 0f; } return ((_childCorners[0].y + _childCorners[1].y) * 0.5f - y) / num; } private static void AttachSpawnHook() { if (_spawnHookAttached) { return; } try { Game.m_playerInitialSpawn += OnPlayerInitialSpawn; _spawnHookAttached = true; } catch (Exception ex) { CustomMainMenuPlugin.LogWarn("Could not subscribe to Game.m_playerInitialSpawn: " + ex.Message); } } private static void DetachSpawnHook() { if (_spawnHookAttached) { try { Game.m_playerInitialSpawn -= OnPlayerInitialSpawn; } catch (Exception ex) { CustomMainMenuPlugin.LogWarn("Could not unsubscribe from Game.m_playerInitialSpawn: " + ex.Message); } _spawnHookAttached = false; } } private static void OnPlayerInitialSpawn() { DetachSpawnHook(); StopCoroutineRef(ref _timeoutCoroutine); MenuMusic.CommitForWorldEntry("initial spawn"); _sessionArmed = false; RestoreLoadingChrome(); LoadingTips.ResetSession(); FadeOutAndClearArt(); } private static IEnumerator TimeoutGuardCoroutine() { yield return (object)new WaitForSecondsRealtime(2400f); _timeoutCoroutine = null; LogLoading("Loading art timeout reached; restoring vanilla loading screen."); MenuMusic.CommitForWorldEntry("load timeout"); StopAllWatchers("timeout"); } private static void StopCoroutineRef(ref Coroutine? coroutine) { if (coroutine != null) { CustomMainMenuPlugin instance = CustomMainMenuPlugin.Instance; if ((Object)(object)instance != (Object)null) { ((MonoBehaviour)instance).StopCoroutine(coroutine); } coroutine = null; } } private static void DestroyArtSprite() { CustomMainMenuPlugin.DestroyOwnedSprite(_artSprite); _artSprite = null; } private static void LogLoading(string message) { CustomMainMenuPlugin.LogInfo(message); } private static Sprite? SpriteFromFile(string absolutePath) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_003e: 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) try { byte[] data = File.ReadAllBytes(absolutePath); Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false); if (!TextureDecode.LoadImage(val, data, markNonReadable: true)) { Object.Destroy((Object)(object)val); return null; } return Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f)); } catch { return null; } } private static void FadeOutAndClearArt() { CustomMainMenuPlugin instance = CustomMainMenuPlugin.Instance; if ((Object)(object)_artImage == (Object)null || (Object)(object)instance == (Object)null) { ClearArt(); DestroyArtSprite(); } else { StopCoroutineRef(ref _fadeCoroutine); _fadeCoroutine = ((MonoBehaviour)instance).StartCoroutine(LoadingFadeCoroutine(_artImage)); } } private static IEnumerator LoadingFadeCoroutine(Image image) { float elapsed = 0f; float startAlpha = ((Graphic)image).canvasRenderer.GetAlpha(); while (elapsed < 0.6f && (Object)(object)image != (Object)null) { elapsed += Time.unscaledDeltaTime; float num = Mathf.Clamp01(elapsed / 0.6f); ((Graphic)image).color = new Color(1f, 1f, 1f, Mathf.Lerp(startAlpha, 0f, num)); ((Graphic)image).SetAllDirty(); yield return null; } ClearArt(); DestroyArtSprite(); _fadeCoroutine = null; } private static void ClearArt() { //IL_002c: Unknown result type (might be due to invalid IL or missing references) Image artImage = _artImage; _artImage = null; if ((Object)(object)artImage != (Object)null) { artImage.sprite = _artOriginalSprite; artImage.overrideSprite = _artOriginalSprite; ((Graphic)artImage).color = _artOriginalColor; ((Graphic)artImage).SetAllDirty(); } _artOriginalSprite = null; } private static void ApplyLoadingImage(Image target, Sprite customSprite) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) target.sprite = customSprite; target.overrideSprite = customSprite; ((Behaviour)target).enabled = true; target.preserveAspect = false; target.type = (Type)0; ((Graphic)target).material = null; ((Graphic)target).color = Color.white; StretchToFullscreen(((Graphic)target).rectTransform); ((Graphic)target).SetAllDirty(); } private static bool Approximately(float a, float b) { return Mathf.Abs(a - b) <= 0.001f; } internal static bool IsFullscreenLike(RectTransform rt) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0023: 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_003a: 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) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)rt == (Object)null) { return false; } bool num = Approximately(rt.anchorMin.x, 0f) && Approximately(rt.anchorMin.y, 0f) && Approximately(rt.anchorMax.x, 1f) && Approximately(rt.anchorMax.y, 1f); bool flag = Approximately(rt.offsetMin.x, 0f) && Approximately(rt.offsetMin.y, 0f) && Approximately(rt.offsetMax.x, 0f) && Approximately(rt.offsetMax.y, 0f); return num && flag; } internal static string GetHierarchyPath(Transform transform) { if ((Object)(object)transform == (Object)null) { return "<null>"; } List<string> list = new List<string>(); Transform val = transform; while ((Object)(object)val != (Object)null) { list.Insert(0, ((Object)val).name); val = val.parent; } return string.Join("/", list.ToArray()); } internal static void StretchToFullscreen(RectTransform rt) { //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_0036: 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_004c: 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) if (!((Object)(object)rt == (Object)null)) { rt.anchorMin = Vector2.zero; rt.anchorMax = Vector2.one; rt.pivot = new Vector2(0.5f, 0.5f); rt.offsetMin = Vector2.zero; rt.offsetMax = Vector2.zero; rt.anchoredPosition = Vector2.zero; ((Transform)rt).localScale = Vector3.one; ((Transform)rt).localRotation = Quaternion.identity; } } } internal static class LoadingTips { internal const string ContentFileName = "customtips.txt"; internal const int MaxTips = 20; private static ConfigEntry<bool>? _useCustomTips; private static ConfigEntry<bool>? _hideTopLetterbox; private static ConfigEntry<bool>? _hideTipBackdrop; private static ConfigEntry<bool>? _useCustomLoadingLogo; private static ConfigEntry<bool>? _showCustomLogoWhenLetterboxHidden; private static readonly List<string> CachedTips = new List<string>(20); private static List<string>? _vanillaTips; private static int _vanillaFromHudId; internal static void BindConfig(ConfigFile config) { _useCustomTips = config.Bind<bool>("Loading Screen", "UseCustomTips", false, "Use tips from CustomMainMenu/customtips.txt on the world-load screen. Off keeps Valheim's own tips. One tip per line, up to " + 20 + ". Blank lines are skipped. Missing or empty file keeps vanilla tips."); _hideTopLetterbox = config.Bind<bool>("Loading Screen", "HideTopLetterbox", false, "Hide the top black letterbox and Valheim's fiery loading logo. The bottom tip bar stays. Restored when you are in-world so sleep/teleport overlays stay vanilla. Use ShowCustomLogoWhenLetterboxHidden if you still want your menu logo on that screen."); _hideTipBackdrop = config.Bind<bool>("Loading Screen", "HideTipBackdrop", false, "Hide the black bar behind loading-screen tips. Tip text, click-to-cycle, and the spinner stay. Restored in-world."); _useCustomLoadingLogo = config.Bind<bool>("Loading Screen", "UseCustomLoadingLogo", false, "Replace Valheim's fiery loading-screen logo with the same logo.png / logo.jpg used on the start menu. Off keeps the vanilla title. Missing file leaves vanilla."); _showCustomLogoWhenLetterboxHidden = config.Bind<bool>("Loading Screen", "ShowCustomLogoWhenLetterboxHidden", false, "If HideTopLetterbox is on, still show the custom loading logo. Does nothing unless UseCustomLoadingLogo is on. Never keeps the vanilla fiery logo."); } internal static bool UseCustomTipsEnabled() { if (CustomMainMenuPlugin.ModEnabled != null && CustomMainMenuPlugin.ModEnabled.Value && _useCustomTips != null) { return _useCustomTips.Value; } return false; } internal static bool HideTopLetterboxEnabled() { if (CustomMainMenuPlugin.ModEnabled != null && CustomMainMenuPlugin.ModEnabled.Value && _hideTopLetterbox != null) { return _hideTopLetterbox.Value; } return false; } internal static bool HideTipBackdropEnabled() { if (CustomMainMenuPlugin.ModEnabled != null && CustomMainMenuPlugin.ModEnabled.Value && _hideTipBackdrop != null) { return _hideTipBackdrop.Value; } return false; } internal static bool UseCustomLoadingLogoEnabled() { if (CustomMainMenuPlugin.ModEnabled != null && CustomMainMenuPlugin.ModEnabled.Value && _useCustomLoadingLogo != null) { return _useCustomLoadingLogo.Value; } return false; } internal static bool ShowCustomLogoWhenLetterboxHiddenEnabled() { if (UseCustomLoadingLogoEnabled() && _showCustomLogoWhenLetterboxHidden != null) { return _showCustomLogoWhenLetterboxHidden.Value; } return false; } internal static bool ShouldShowCustomLoadingLogo() { if (!UseCustomLoadingLogoEnabled()) { return false; } if (HideTopLetterboxEnabled()) { return ShowCustomLogoWhenLetterboxHiddenEnabled(); } return true; } internal static bool HasCustomTips() { if (UseCustomTipsEnabled()) { return CachedTips.Count > 0; } return false; } internal static string GetContentFilePath() { return CustomMainMenuPlugin.GetCustomTipsPath(); } internal static void ReloadFromDisk() { CachedTips.Clear(); if (!UseCustomTipsEnabled()) { return; } try { string contentFilePath = GetContentFilePath(); if (!File.Exists(contentFilePath)) { CustomMainMenuPlugin.LogInfo("Custom loading tips enabled but customtips.txt was not found; vanilla tips stay."); return; } bool flag = false; string[] array = File.ReadAllLines(contentFilePath, Encoding.UTF8); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.Length != 0) { if (CachedTips.Count >= 20) { flag = true; break; } CachedTips.Add(text); } } if (CachedTips.Count == 0) { CustomMainMenuPlugin.LogInfo("Custom loading tips file is empty; vanilla tips stay."); } else { CustomMainMenuPlugin.LogInfo("Custom loading tips loaded: " + CachedTips.Count + (flag ? (" (extra lines beyond " + 20 + " ignored).") : ".")); } } catch (Exception ex) { CachedTips.Clear(); CustomMainMenuPlugin.LogWarn("Custom loading tips could not be read: " + ex.Message); } } internal static void CaptureVanillaIfNeeded(Hud? hud) { if (UseCustomTipsEnabled() && !((Object)(object)hud == (Object)null) && hud.m_loadingTips != null) { int instanceID = ((Object)hud).GetInstanceID(); if (_vanillaTips == null || _vanillaFromHudId != instanceID) { _vanillaTips = new List<string>(hud.m_loadingTips); _vanillaFromHudId = instanceID; } } } internal static void ApplyToShuffle(Hud? hud) { if (!((Object)(object)hud == (Object)null) && hud.m_loadingTips != null && HasCustomTips()) { CaptureVanillaIfNeeded(hud); hud.m_loadingTips.Clear(); hud.m_loadingTips.AddRange(CachedTips); } } internal static void RestoreVanilla(Hud? hud) { if (_vanillaTips != null && !((Object)(object)hud == (Object)null) && hud.m_loadingTips != null && ((Object)hud).GetInstanceID() == _vanillaFromHudId) { hud.m_loadingTips.Clear(); hud.m_loadingTips.AddRange(_vanillaTips); } } internal static void ResetSession() { RestoreVanilla(Hud.instance); } internal static string DescribeConfig() { return "UseCustomTips=" + UseCustomTipsEnabled() + ", HideTopLetterbox=" + HideTopLetterboxEnabled() + ", HideTipBackdrop=" + HideTipBackdropEnabled() + ", UseCustomLoadingLogo=" + UseCustomLoadingLogoEnabled() + ", ShowCustomLogoWhenLetterboxHidden=" + ShowCustomLogoWhenLetterboxHiddenEnabled() + "."; } } internal static class LogoPlacement { private static ConfigEntry<float>? _menuOffsetY; private static ConfigEntry<float>? _loadingOffsetY; private static Image? _menuLogo; private static Vector2 _menuBaseline; private static int _menuLogoId; internal static float MenuOffsetY => _menuOffsetY?.Value ?? 0f; internal static float LoadingOffsetY => _loadingOffsetY?.Value ?? 0f; internal static void BindConfig(ConfigFile config) { if (_menuOffsetY != null) { _menuOffsetY.SettingChanged -= OnMenuOffsetChanged; } _menuOffsetY = config.Bind<float>("Main Menu", "LogoOffsetY", 0f, "Vertical offset for the start-menu logo, in UI units. 0 is the current default placement. Negative moves the logo down, positive moves it up."); _loadingOffsetY = config.Bind<float>("Loading Screen", "LoadingLogoOffsetY", 0f, "Vertical offset for the world-load logo, in UI units. 0 is the current default placement. Negative moves the logo down, positive moves it up. Only applies when that logo is shown."); _menuOffsetY.SettingChanged += OnMenuOffsetChanged; } internal static void ApplyMenuOffset(Image? logo) { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)logo == (Object)null) && !((Object)(object)((Graphic)logo).rectTransform == (Object)null)) { int instanceID = ((Object)logo).GetInstanceID(); if (_menuLogoId != instanceID) { _menuBaseline = ((Graphic)logo).rectTransform.anchoredPosition; _menuLogoId = instanceID; } _menuLogo = logo; SetY(((Graphic)logo).rectTransform, _menuBaseline, MenuOffsetY); } } internal static void ApplyLoadingOffset(RectTransform? rt, Vector2 baseline) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) SetY(rt, baseline, LoadingOffsetY); } internal static void ResetMenuSession() { //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) _menuLogo = null; _menuLogoId = 0; _menuBaseline = Vector2.zero; } internal static string DescribeConfig() { return "LogoOffsetY=" + MenuOffsetY + ", LoadingLogoOffsetY=" + LoadingOffsetY + "."; } private static void OnMenuOffsetChanged(object sender, EventArgs e) { if (CustomMainMenuPlugin.MainMenuActive) { Image menuLogo = _menuLogo; if (!((Object)(object)menuLogo == (Object)null)) { ApplyMenuOffset(menuLogo); } } } private static void SetY(RectTransform? rt, Vector2 baseline, float offsetY) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000b: 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) if (!((Object)(object)rt == (Object)null)) { Vector2 anchoredPosition = baseline; anchoredPosition.y += offsetY; rt.anchoredPosition = anchoredPosition; } } } internal static class MenuAmbientMute { private struct MutedSource { public AudioSource Source; public float Volume; public bool Mute; } private static ConfigEntry<bool>? _muteAmbient; private static readonly List<MutedSource> _muted = new List<MutedSource>(); private static readonly HashSet<int> _mutedIds = new HashSet<int>(); private static readonly List<AudioSource> _scratch = new List<AudioSource>(); internal static void BindConfig(ConfigFile config) { if (_muteAmbient != null) { _muteAmbient.SettingChanged -= OnMuteSettingChanged; } _muteAmbient = config.Bind<bool>("Main Menu", "MuteAmbient", true, "Mute ocean, wind, and looping biome ambient on the start menu. Button clicks still follow the Effects slider. Off restores vanilla menu ambient."); TryMigrateMuteAmbientFromRetiredSection(config); _muteAmbient.SettingChanged += OnMuteSettingChanged; } private static void TryMigrateMuteAmbientFromRetiredSection(ConfigFile config) { if (_muteAmbient != null) { string configFilePath = config.ConfigFilePath; if (!string.IsNullOrEmpty(configFilePath) && File.Exists(configFilePath) && !TryReadIniBool(configFilePath, "Main Menu", "MuteAmbient", out var _) && TryReadIniBool(configFilePath, "Menu Scenery", "MuteAmbient", out var value2)) { _muteAmbient.Value = value2; } } } private static bool TryReadIniBool(string path, string section, string key, out bool value) { value = false; string[] array; try { array = File.ReadAllLines(path); } catch { return false; } bool flag = false; string b = "[" + section + "]"; for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.Length == 0 || text[0] == '#' || text[0] == ';') { continue; } if (text[0] == '[') { flag = string.Equals(text, b, StringComparison.Ordinal); } else { if (!flag || !text.StartsWith(key, StringComparison.Ordinal)) { continue; } int num = text.IndexOf('='); if (num > 0 && string.Equals(text.Substring(0, num).Trim(), key, StringComparison.Ordinal)) { string text2 = text.Substring(num + 1).Trim(); int num2 = text2.IndexOfAny(new char[4] { ' ', '\t', '#', ';' }); if (num2 > 0) { text2 = text2.Substring(0, num2); } return bool.TryParse(text2, out value); } } } return false; } internal static void Shutdown() { if (_muteAmbient != null) { _muteAmbient.SettingChanged -= OnMuteSettingChanged; } Restore(); } private static bool ShouldMute() { if (CustomMainMenuPlugin.IsActive && CustomMainMenuPlugin.MainMenuActive) { if (_muteAmbient != null) { return _muteAmbient.Value; } return true; } return false; } internal static void Apply() { if (!ShouldMute()) { Restore(); } else { MuteStartSceneAmbient(); } } internal static void Restore() { for (int i = 0; i < _muted.Count; i++) { MutedSource mutedSource = _muted[i]; if (!((Object)(object)mutedSource.Source == (Object)null)) { mutedSource.Source.volume = mutedSource.Volume; mutedSource.Source.mute = mutedSource.Mute; } } _muted.Clear(); _mutedIds.Clear(); } private static void OnMuteSettingChanged(object sender, EventArgs e) { Apply(); } private static void MuteStartSceneAmbient() { //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) try { FejdStartup instance = FejdStartup.instance; if ((Object)(object)instance == (Object)null) { return; } AudioMan instance2 = AudioMan.instance; AudioMixerGroup gui = (((Object)(object)instance2 != (Object)null) ? instance2.m_guiMixer : null); AudioMixerGroup ambient = (((Object)(object)instance2 != (Object)null) ? instance2.m_ambientMixer : null); AudioMixerGroup music = (((Object)(object)MusicMan.instance != (Object)null) ? MusicMan.instance.m_musicMixer : null); int num = 0; Scene scene = ((Component)instance).gameObject.scene; GameObject[] rootGameObjects = ((Scene)(ref scene)).GetRootGameObjects(); for (int i = 0; i < rootGameObjects.Length; i++) { rootGameObjects[i].GetComponentsInChildren<AudioSource>(true, _scratch); for (int j = 0; j < _scratch.Count; j++) { AudioSource src = _scratch[j]; if (ShouldMuteSceneSource(src, gui, ambient, music) && RememberAndSilence(src)) { num++; } } } _scratch.Clear(); if (num > 0 && CustomMainMenuPlugin.IsVerboseLogging) { CustomMainMenuPlugin.LogInfo($"Menu ambient muted ({num} scene source(s))."); } } catch (Exception ex) { _scratch.Clear(); CustomMainMenuPlugin.LogInfo("Menu ambient scene mute skipped: " + ex.Message); } } private static bool ShouldMuteSceneSource(AudioSource? src, AudioMixerGroup? gui, AudioMixerGroup? ambient, AudioMixerGroup? music) { if ((Object)(object)src == (Object)null) { return false; } AudioMixerGroup outputAudioMixerGroup = src.outputAudioMixerGroup; if (IsGuiOrMusicGroup(outputAudioMixerGroup, gui, music)) { return false; } if ((Object)(object)ambient != (Object)null && (Object)(object)outputAudioMixerGroup == (Object)(object)ambient) { return true; } if ((Object)(object)outputAudioMixerGroup != (Object)null && GroupNameContains(outputAudioMixerGroup, "Ambient")) { return true; } if (!src.loop) { if (src.isPlaying) { return src.spatialBlend > 0.05f; } return false; } return true; } private static bool IsGuiOrMusicGroup(AudioMixerGroup? group, AudioMixerGroup? gui, AudioMixerGroup? music) { if ((Object)(object)group == (Object)null) { return false; } if ((Object)(object)gui != (Object)null && (Object)(object)group == (Object)(object)gui) { return true; } if ((Object)(object)music != (Object)null && (Object)(object)group == (Object)(object)music) { return true; } if (!GroupNameContains(group, "GUI")) { return GroupNameContains(group, "Music"); } return true; } private static bool GroupNameContains(AudioMixerGroup group, string token) { string name = ((Object)group).name; if (!string.IsNullOrEmpty(name)) { return name.IndexOf(token, StringComparison.OrdinalIgnoreCase) >= 0; } return false; } private static bool RememberAndSilence(AudioSource? src) { if ((Object)(object)src == (Object)null) { return false; } int instanceID = ((Object)src).GetInstanceID(); if (!_mutedIds.Add(instanceID)) { src.volume = 0f; src.mute = true; return false; } _muted.Add(new MutedSource { Source = src, Volume = src.volume, Mute = src.mute }); src.volume = 0f; src.mute = true; return true; } } internal static class MenuAtmosphere { private const float NightTimeFraction = 0.875f; private const float SunAngle = 45f; private static readonly Color FogColor = new Color(0.047f, 0.165f, 0.196f, 1f); private static readonly Color AmbientLight = new Color(0.191f, 0.269f, 0.36f, 1f); private static readonly Color SunColor = new Color(0.337f, 0.729f, 0.714f, 1f); private const float FogDensity = 0.08f; private const float SunIntensity = 0.05f; private const float ValheimLitSunIntensity = 0.7f; private static bool _applied; internal static IEnumerator ApplyWhenReady() { _applied = false; for (int i = 0; i < 8; i++) { if (!CustomMainMenuPlugin.MainMenuActive) { break; } if (!CustomMainMenuPlugin.IsActive) { break; } if (TryApply()) { yield break; } yield return null; } if (CustomMainMenuPlugin.MainMenuActive && !_applied) { CustomMainMenuPlugin.LogInfo("Menu atmosphere: no directional light on the start scene; night look was not applied."); } } private static bool TryApply() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: 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_007c: 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_0088: 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_00a4: 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_00aa: 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_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: 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_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: 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) Light val = ResolveSun(); if ((Object)(object)val == (Object)null) { return false; } RenderSettings.fog = true; RenderSettings.fogColor = FogColor; RenderSettings.fogDensity = 0.08f; RenderSettings.ambientMode = (AmbientMode)3; RenderSettings.ambientLight = AmbientLight; Quaternion val2 = Quaternion.Euler(-45f, 0f, 0f) * Quaternion.Euler(0f, -90f, 0f) * Quaternion.Euler(225f, 0f, 0f); ((Component)val).transform.rotation = val2; val.intensity = 0.05f; val.color = SunColor; Vector3 val3 = -(val2 * Vector3.forward); Shader.SetGlobalVector("_SunDir", Vector4.op_Implicit(val3)); Shader.SetGlobalVector("_SkyboxSunDir", Vector4.op_Implicit(val3)); Shader.SetGlobalColor("_SunFogColor", FogColor); Shader.SetGlobalColor("_SunColor", SunColor * 0.7f); Shader.SetGlobalColor("_AmbientColor", AmbientLight); Shader.SetGlobalFloat("_Wet", 0f); _applied = true; if (CustomMainMenuPlugin.IsVerboseLogging) { CustomMainMenuPlugin.LogInfo($"Menu atmosphere applied (night, fog density {0.08f:0.###}, fraction {0.875f:0.###})."); } return true; } private static Light? ResolveSun() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Invalid comparison between Unknown and I4 //IL_005f: 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_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: 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) //IL_0086: Invalid comparison between Unknown and I4 //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Invalid comparison between Unknown and I4 //IL_00bb: 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) FejdStartup instance = FejdStartup.instance; if ((Object)(object)instance == (Object)null) { return null; } Scene scene = ((Component)instance).gameObject.scene; Light sun = RenderSettings.sun; if ((Object)(object)sun != (Object)null && (int)sun.type == 1 && ((Component)sun).gameObject.scene == scene) { return sun; } GameObject val = GameObject.Find("Directional Light"); if ((Object)(object)val != (Object)null && val.scene == scene) { Light component = val.GetComponent<Light>(); if ((Object)(object)component != (Object)null && (int)component.type == 1) { return component; } } Light[] array = Object.FindObjectsByType<Light>((FindObjectsInactive)1, (FindObjectsSortMode)0); foreach (Light val2 in array) { if ((Object)(object)val2 != (Object)null && (int)val2.type == 1 && ((Component)val2).gameObject.scene == scene) { return val2; } } return null; } } internal static class MenuChangelog { internal const string ContentFileName = "changelog.txt"; private static ConfigEntry<bool>? _enabled; private static ConfigEntry<bool>? _useCustomText; private static ConfigEntry<bool>? _overrideVanillaText; private static ConfigEntry<bool>? _autoOpen; private static ConfigEntry<string>? _titleText; private static string _customText = string.Empty; private static FileSystemWatcher? _watcher; private static ChangeLog? _changeLog; private static TMP_Text? _titleLabel; private static Text? _titleTextLabel; private static string _vanillaTitle = string.Empty; pr