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 CustomMainMenu v1.3.0
plugins/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.Security; using System.Security.Permissions; 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.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, and optional skip of the 1.0 startup intro.")] [assembly: AssemblyFileVersion("1.3.0.0")] [assembly: AssemblyInformationalVersion("1.3.0")] [assembly: AssemblyProduct("CustomMainMenu")] [assembly: AssemblyTitle("CustomMainMenu")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.3.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.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[5] { "General", "Startup", "Main Menu", "Changelog", "Discord Panel" }; internal static bool IsOurs(ConfigFile config) { if ((Object)(object)CustomMainMenuPlugin.Instance != (Object)null) { return config == ((BaseUnityPlugin)CustomMainMenuPlugin.Instance).Config; } return false; } internal static void Write(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); } string configFilePath = config.ConfigFilePath; string directoryName = Path.GetDirectoryName(configFilePath); if (!string.IsNullOrEmpty(directoryName)) { Directory.CreateDirectory(directoryName); } using StreamWriter streamWriter = new StreamWriter(configFilePath, append: false, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)); streamWriter.NewLine = "\r\n"; streamWriter.WriteLine("## Settings file was created by plugin Custom Main Menu v1.3.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); } } } 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(); } } } internal static class Hu { internal const string StartGameLower = "játék indítása"; internal const string SettingsLower = "beállitások"; internal const string PatchNotesTitleLower = "frissítési napló"; internal const string ModdedMarkerLower = "módosított"; } [BepInPlugin("rdmods.custommainmenu", "Custom Main Menu", "1.3.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.3.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 const float MenuMusicBaseGain = 0.72f; internal static bool MenuMusicFadeCommittedForSession; internal static volatile bool MainMenuActive; internal static readonly bool EnableCustomLoadingScreens = true; private ConfigEntry<bool>? _enableLogging; private Harmony? _harmony; private GameObject? _menuMusicGo; private AudioSource? _menuMusicSource; private Coroutine? _menuMusicFadeCoroutine; private Coroutine? _menuMusicStartCoroutine; private Coroutine? _menuAtmosphereApplyCoroutine; private bool _menuMusicPausedForCinematic; private float _menuMusicFadeFactor = 1f; private static GameObject? _discordPanel; private static GameObject? _discordJoinButton; private Coroutine? _discordLogoCoroutine; private static Image? _discordLogoImage; 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 HashSet<string> NeverHide = new HashSet<string>(StringComparer.Ordinal) { "startgui", "worldpanel", "startgame", "topleft", "topright", "menu", "joinpanel", "panel", "tooltips", "eula", "eulawindow", "showeula", "version", "versionlabel", "cinematics", "cinematicslist" }; internal static CustomMainMenuPlugin? Instance { get; private set; } internal static ConfigEntry<bool>? ModEnabled { get; private set; } internal static ManualLogSource? ModLog { get; private set; } internal static GameObject? DiscordPanelRoot => _discordPanel; internal static bool IsVerboseLogging { get { if (Instance?._enableLogging != null) { return Instance._enableLogging.Value; } return false; } } internal static void SetDiscordPanelRoot(GameObject? panel) { _discordPanel = panel; } private void Awake() { //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Expected O, but got Unknown Instance = this; ModLog = ((BaseUnityPlugin)this).Logger; MenuMusicFadeCommittedForSession = false; 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."); _enableLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableLogging", false, "Write informational logs (menu lifecycle, loading watcher, atmosphere, Discord visibility). Delete cfg after changing."); SkipStartupIntro.BindConfig(((BaseUnityPlugin)this).Config); MenuAmbientMute.BindConfig(((BaseUnityPlugin)this).Config); MenuChangelog.BindConfig(((BaseUnityPlugin)this).Config); DiscordPanelVisibility.BindConfig(((BaseUnityPlugin)this).Config); ConfigLayout.Write(((BaseUnityPlugin)this).Config); ((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet; 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.3.0 loaded. Asset root: " + GetAssetRootPath())); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Loading art folder: " + GetResolvedLoadingArtFolder())); ((BaseUnityPlugin)this).Logger.LogInfo((object)SkipStartupIntro.DescribeConfig()); } private void OnDestroy() { SkipStartupIntro.ResetSession(); MenuAmbientMute.Shutdown(); MenuChangelog.Shutdown(); LoadingScreens.StopAllWatchers("plugin destroyed"); if (_discordLogoCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_discordLogoCoroutine); _discordLogoCoroutine = null; } if (_menuMusicFadeCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_menuMusicFadeCoroutine); _menuMusicFadeCoroutine = null; } if (_menuMusicStartCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_menuMusicStartCoroutine); _menuMusicStartCoroutine = null; } if (_menuAtmosphereApplyCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_menuAtmosphereApplyCoroutine); _menuAtmosphereApplyCoroutine = null; } MenuAtmosphere.Clear(); DestroyDiscordPanel(); MainMenuActive = false; StopMenuMusic(); Harmony? harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } _harmony = null; Instance = null; } internal static bool TryGetLocalPlayerPresentPlain() { try { return (Object)(object)Player.m_localPlayer != (Object)null; } catch { return false; } } internal static void CommitMenuMusicFadeForWorldEntry(string trigger) { if (ModEnabled != null && ModEnabled.Value && !MenuMusicFadeCommittedForSession) { MenuMusicFadeCommittedForSession = true; LogInfo("Stopping menu music (" + trigger + ")."); FadeOutAndStopMenuMusic(); } } internal static bool IsWorldFullyLoaded() { bool num = TryGetLocalPlayerPresentPlain(); if (num && !MenuMusicFadeCommittedForSession) { CommitMenuMusicFadeForWorldEntry("local player in world"); } return num; } 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 bool HasCustomMenuMusic() { try { return File.Exists(GetMusicPath()); } catch { return false; } } 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? 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) || new FileInfo(text).Length != new FileInfo(text2).Length) { File.Copy(text, text2, overwrite: true); } } } catch (Exception ex) { LogWarn("How-to customize file could not be copied: " + ex.Message); } } private static void CachePluginDefaultsRoot() { try { CustomMainMenuPlugin? instance = Instance; string text = ((instance != 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(); MenuAtmosphere.ResetForMenuSession(); StopMusicManWorldMusicForMainMenu(); MenuAmbientMute.Apply(); if ((Object)(object)Instance == (Object)null) { MenuAtmosphere.Apply(); EnsureDiscordPanel(fejd, "main menu opened"); return; } if (Instance._menuAtmosphereApplyCoroutine != null) { ((MonoBehaviour)Instance).StopCoroutine(Instance._menuAtmosphereApplyCoroutine); Instance._menuAtmosphereApplyCoroutine = null; } Instance._menuAtmosphereApplyCoroutine = ((MonoBehaviour)fejd).StartCoroutine(MenuAtmosphere.ApplyWhenReady()); EnsureDiscordPanel(fejd, "main menu opened"); } internal static void OnWorldLoadCommitted() { LoadingScreens.StopAllWatchers("world load committed"); MenuAmbientMute.Restore(); DestroyDiscordPanel(); MenuAtmosphere.Clear(); LogInfo("Menu atmosphere cleared (world load)."); if (Instance?._menuAtmosphereApplyCoroutine != null) { ((MonoBehaviour)Instance).StopCoroutine(Instance._menuAtmosphereApplyCoroutine); Instance._menuAtmosphereApplyCoroutine = null; } MainMenuActive = false; try { MusicMan instance = MusicMan.instance; if ((Object)(object)instance != (Object)null) { MusicManAccess.ReleaseMainMenuMute(instance); } } catch (Exception ex) { ManualLogSource? modLog = ModLog; if (modLog != null) { modLog.LogError((object)ex); } } } internal static void SilenceMusicManForMainMenu() { try { MusicMan instance = MusicMan.instance; if (!((Object)(object)instance == (Object)null)) { MusicManAccess.SilenceForMainMenu(instance); } } catch (Exception ex) { ManualLogSource? modLog = ModLog; if (modLog != null) { modLog.LogError((object)ex); } } } private static void StopMusicManWorldMusicForMainMenu() { SilenceMusicManForMainMenu(); LogInfo("MusicMan world music cleared for main menu."); } internal static void PresentBrandedMenu(FejdStartup fejd, string reason) { LogInfo("Main menu presented (" + reason + ")."); OnMainMenuOpened(fejd); ApplyMenuChrome(fejd); MenuChangelog.ApplyOnMenuOpen(fejd); StartMenuMusic(); } internal static void HideTitleLogo(FejdStartup fejd) { if (!((Object)(object)fejd == (Object)null)) { Transform val = FindDeepNorthLogoRoot(((Component)fejd).transform); 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)) { HideMenuClutterOnce(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 HideMenuClutterOnce(FejdStartup fejd) { HideSerializedClutter(fejd); HideVanillaDiscordButton(fejd); HideNamedClutterUnder(((Object)(object)fejd.m_menuList != (Object)null) ? fejd.m_menuList.transform : null); HideNamedClutterUnder(((Object)(object)fejd.m_mainMenu != (Object)null) ? fejd.m_mainMenu.transform : null); if ((Object)(object)fejd.m_merchStoreButtonParent != (Object)null) { HideNamedClutterUnder(fejd.m_merchStoreButtonParent.transform); } } 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 HideNamedClutterUnder(Transform? root) { if (!((Object)(object)root == (Object)null)) { HideNamedClutterWalk(root); } } private static void HideNamedClutterWalk(Transform tr) { GameObject gameObject = ((Component)tr).gameObject; string text = (((Object)gameObject).name ?? string.Empty).ToLowerInvariant(); if (!NeverHide.Contains(text) && !IsProtectedMenuObject(text)) { string lowerOwn = string.Empty; bool flag; switch (text) { case "topic": case "url": case "text": case "modded_text": flag = true; break; default: flag = false; break; } if (flag) { lowerOwn = GetOwnText(gameObject).ToLowerInvariant(); } if (ShouldHideUi(text, lowerOwn) && gameObject.activeSelf) { gameObject.SetActive(false); } } for (int i = 0; i < tr.childCount; i++) { HideNamedClutterWalk(tr.GetChild(i)); } } private static void HideVanillaDiscordButton(FejdStartup fejd) { HideVanillaDiscordUnder(fejd.m_menuList); if ((Object)(object)fejd.m_menuList != (Object)(object)fejd.m_mainMenu) { HideVanillaDiscordUnder(fejd.m_mainMenu); } } private static void HideVanillaDiscordUnder(GameObject? root) { if ((Object)(object)root == (Object)null) { return; } Button[] componentsInChildren = root.GetComponentsInChildren<Button>(true); foreach (Button val in componentsInChildren) { if ((Object)(object)((val != null) ? ((Component)val).gameObject : null) == (Object)null || IsOurDiscordUi(((Component)val).gameObject)) { continue; } bool flag = false; int persistentEventCount = ((UnityEventBase)val.onClick).GetPersistentEventCount(); for (int j = 0; j < persistentEventCount; j++) { if (string.Equals(((UnityEventBase)val.onClick).GetPersistentMethodName(j), "OnButtonDiscord", StringComparison.Ordinal)) { flag = true; break; } } if ((((Object)((Component)val).gameObject).name ?? string.Empty).ToLowerInvariant().Contains("discord")) { flag = true; } if (flag) { ((Component)val).gameObject.SetActive(false); LogInfo("Vanilla Discord control hidden (" + ((Object)((Component)val).gameObject).name + ")."); } } } 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; } private static void HideGo(GameObject? go, string label) { if (!((Object)(object)go == (Object)null) && go.activeSelf) { go.SetActive(false); LogInfo("Hidden " + label + " (" + ((Object)go).name + ")."); } } private static bool IsProtectedMenuObject(string lowerName) { if (!lowerName.Contains("eula") && !lowerName.Contains("cinematic")) { return lowerName.Contains("version"); } return true; } private static bool ShouldHideUi(string lowerName, string lowerOwn) { if (IsProtectedMenuObject(lowerName)) { return false; } bool flag; switch (lowerName) { case "canvas changelog": case "changelog": case "topic": case "patchlogscroll": flag = true; break; default: flag = false; break; } if (flag || lowerOwn == "frissítési napló") { return !MenuChangelog.ShouldShowPanel(); } if (lowerName.Contains("merch") || lowerName == "valheim.shop" || lowerName == "url" || lowerOwn == "valheim.shop") { return true; } if (lowerName == "modded_text" || lowerOwn.Contains("currently playing a modded version") || lowerOwn.Contains("modded version") || lowerOwn.Contains("módosított")) { return true; } return false; } private static string GetOwnText(GameObject go) { TextMeshProUGUI component = go.GetComponent<TextMeshProUGUI>(); if ((Object)(object)component != (Object)null && !string.IsNullOrWhiteSpace(((TMP_Text)component).text)) { return ((TMP_Text)component).text.Trim(); } Text component2 = go.GetComponent<Text>(); if (!((Object)(object)component2 != (Object)null) || string.IsNullOrWhiteSpace(component2.text)) { return string.Empty; } return component2.text.Trim(); } private static bool ApplyCustomLogo(FejdStartup fejd) { Transform val = FindDeepNorthLogoRoot(((Component)fejd).transform); if ((Object)(object)val != (Object)null) { HideLogoVfxChildren(val); if (!((Component)val).gameObject.activeSelf) { ((Component)val).gameObject.SetActive(true); } } if (!File.Exists(GetLogoPath())) { 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}). EnableLogging dumps named logo objects."); } return (Object)(object)best != (Object)null; } Sprite val2 = LoadSpriteFromDisk(GetLogoPath()); 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); LogInfo($"Logo applied (score {hi}, name={((Object)((Component)best).gameObject).name})."); return true; } private static Transform? FindDeepNorthLogoRoot(Transform root) { string text = (((Object)root).name ?? string.Empty).ToLowerInvariant(); if (text.Contains("logo") && text.Contains("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 void FindBestLogoImage(Transform root, ref Image? best, ref int hi) { Image component = ((Component)root).GetComponent<Image>(); if ((Object)(object)component != (Object)null) { string text = (((Object)root).name ?? string.Empty).ToLowerInvariant(); string text2 = (((Object)(object)component.sprite != (Object)null) ? ((Object)component.sprite).name.ToLowerInvariant() : string.Empty); if (text.Contains("logo") || text.Contains("valheim") || text.Contains("title") || text2.Contains("logo") || text2.Contains("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 text = (((Object)((Component)img).gameObject).name ?? "").ToLowerInvariant(); string text2 = (((Object)(object)img.sprite != (Object)null) ? ((Object)img.sprite).name.ToLowerInvariant() : ""); if (!text.Contains("logo") && !text.Contains("valheim") && !text.Contains("title") && !text2.Contains("logo")) { return text2.Contains("valheim"); } return true; } private static int ScoreLogo(Image img) { //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) string text = (((Object)((Component)img).gameObject).name ?? "").ToLowerInvariant(); string text2 = (((Object)(object)img.sprite != (Object)null) ? ((Object)img.sprite).name.ToLowerInvariant() : ""); int num = 0; if (text.Contains("logo")) { num += 100; } if (text.Contains("deepnorth")) { num += 40; } if (text.Contains("valheim") || text2.Contains("valheim")) { num += 80; } if (text.Contains("title")) { num += 40; } if (text2.Contains("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; } private 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; } } private static void HideLogoVfxChildren(Transform logoRoot) { for (int i = 0; i < logoRoot.childCount; i++) { HideLogoVfxWalk(logoRoot.GetChild(i)); } } private static void HideLogoVfxWalk(Transform child) { string text = (((Object)child).name ?? string.Empty).ToLowerInvariant(); if ((text.Contains("snow") || text.Contains("ember") || text.Contains("particle") || text.Contains("snowblow")) && ((Component)child).gameObject.activeSelf) { ((Component)child).gameObject.SetActive(false); LogInfo("Deep North logo VFX hidden (" + ((Object)child).name + ")."); } for (int i = 0; i < child.childCount; i++) { HideLogoVfxWalk(child.GetChild(i)); } } private 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 (((Object)font).name ?? string.Empty).IndexOf("LiberationSans", StringComparison.OrdinalIgnoreCase) < 0; } 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; LogInfo("Discord title font: " + ((Object)val).name); } else { LogWarn("Discord title: no usable TMP font found yet."); } } 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) { TextMeshProUGUI[] componentsInChildren = Resources.FindObjectsOfTypeAll<TextMeshProUGUI>(); foreach (TextMeshProUGUI val3 in componentsInChildren) { if ((Object)(object)val3 != (Object)null && IsUsableTmpFont(((TMP_Text)val3).font)) { val = ((TMP_Text)val3).font; break; } } } if ((Object)(object)val == (Object)null) { TMP_FontAsset[] array = Resources.FindObjectsOfTypeAll<TMP_FontAsset>(); foreach (TMP_FontAsset val4 in array) { if (IsUsableTmpFont(val4)) { val = val4; 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() { if (Instance?._discordLogoCoroutine != null) { ((MonoBehaviour)Instance).StopCoroutine(Instance._discordLogoCoroutine); Instance._discordLogoCoroutine = null; } if ((Object)(object)_discordPanel != (Object)null) { Object.Destroy((Object)(object)_discordPanel); } _discordPanel = null; _discordJoinButton = null; _discordLogoImage = null; DiscordPanelVisibility.ResetSessionState(); } private static void EnsureParentedToStartMenuCanvas(FejdStartup fejd) { if (!((Object)(object)_discordPanel == (Object)null)) { 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>()); 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 TextMeshProUGUI? FindBestMenuTmpText(FejdStartup startup) { if ((Object)(object)startup == (Object)null) { return null; } TextMeshProUGUI result = null; int num = int.MinValue; TextMeshProUGUI[] componentsInChildren = ((Component)startup).GetComponentsInChildren<TextMeshProUGUI>(true); foreach (TextMeshProUGUI val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && IsUsableTmpFont(((TMP_Text)val).font)) { string text = (((TMP_Text)val).text ?? "").ToLowerInvariant(); string text2 = (((Object)((Component)val).gameObject).name ?? "").ToLowerInvariant(); int num2 = 0; if (text.Contains("start game") || text.Contains("játék indítása")) { num2 += 300; } if (text.Contains("beállitások") || text.Contains("settings")) { num2 += 220; } if (text2.Contains("text")) { num2 += 20; } if (((TMP_Text)val).fontSize >= 20f) { num2 += 15; } if (num2 > num) { num = num2; result = val; } } } return result; } internal static Button? FindBestStartMenuButton(FejdStartup startup) { //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) 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; } Button result = null; int num = int.MinValue; Button[] componentsInChildren = val.GetComponentsInChildren<Button>(true); foreach (Button val2 in componentsInChildren) { if ((Object)(object)val2 == (Object)null || IsOurDiscordUi(((Component)val2).gameObject)) { continue; } string text = GetButtonText(((Component)val2).gameObject).ToLowerInvariant(); string text2 = (((Object)((Component)val2).gameObject).name ?? "").ToLowerInvariant(); int num2 = 0; if (text.Contains("start game") || text.Contains("játék indítása")) { num2 += 300; } if (text.Contains("start") || text2.Contains("start")) { num2 += 150; } if (text.Contains("play")) { num2 += 120; } RectTransform component = ((Component)val2).GetComponent<RectTransform>(); if ((Object)(object)component != (Object)null) { Rect rect = component.rect; float width = ((Rect)(ref rect)).width; rect = component.rect; if (Mathf.Abs(width * ((Rect)(ref rect)).height) > 5000f) { num2 += 10; } } if (num2 > num) { num = num2; result = val2; } } return result; } private static string GetButtonText(GameObject go) { TextMeshProUGUI[] componentsInChildren = go.GetComponentsInChildren<TextMeshProUGUI>(true); foreach (TextMeshProUGUI val in componentsInChildren) { if (!string.IsNullOrWhiteSpace(((TMP_Text)val).text)) { return ((TMP_Text)val).text; } } Text[] componentsInChildren2 = go.GetComponentsInChildren<Text>(true); foreach (Text val2 in componentsInChildren2) { if (!string.IsNullOrWhiteSpace(val2.text)) { return val2.text; } } return ""; } 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_0080: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Expected O, but got Unknown //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Unknown result type (might be due to invalid IL or missing references) //IL_01c3: Unknown result type (might be due to invalid IL or missing references) //IL_01c9: Expected O, but got Unknown //IL_0215: Unknown result type (might be due to invalid IL or missing references) //IL_0245: Unknown result type (might be due to invalid IL or missing references) //IL_024a: Unknown result type (might be due to invalid IL or missing references) //IL_025c: Unknown result type (might be due to invalid IL or missing references) //IL_0263: Unknown result type (might be due to invalid IL or missing references) //IL_026e: Unknown result type (might be due to invalid IL or missing references) //IL_0279: Unknown result type (might be due to invalid IL or missing references) //IL_027e: Unknown result type (might be due to invalid IL or missing references) //IL_027f: Unknown result type (might be due to invalid IL or missing references) //IL_0285: Unknown result type (might be due to invalid IL or missing references) //IL_02c1: 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); 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; TextMeshProUGUI template = FindBestMenuTmpText(fejd); GameObject val2 = new GameObject("DiscordTitle", new Type[1] { typeof(RectTransform) }); val2.SetActive(false); val2.transform.SetParent(_discordPanel.transform, false); NormalizeRectForLayoutGroup(val2.GetComponent<RectTransform>()); LayoutElement obj2 = val2.AddComponent<LayoutElement>(); obj2.flexibleWidth = 1f; obj2.preferredWidth = 300f; obj2.preferredHeight = 90f; TextMeshProUGUI obj3 = val2.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); val2.SetActive(true); GameObject val3 = new GameObject("DiscordLogo"); val3.transform.SetParent(_discordPanel.transform, false); LayoutElement obj4 = val3.AddComponent<LayoutElement>(); obj4.preferredHeight = 150f; obj4.preferredWidth = 300f; _discordLogoImage = val3.AddComponent<Image>(); _discordLogoImage.type = (Type)0; ((Graphic)_discordLogoImage).color = Color.white; ((Graphic)_discordLogoImage).material = null; _discordLogoImage.preserveAspect = true; ((Graphic)_discordLogoImage).raycastTarget = false; GameObject val4 = new GameObject("DiscordGlyph"); val4.transform.SetParent(val3.transform, false); RectTransform obj5 = val4.AddComponent<RectTransform>(); obj5.anchorMin = Vector2.zero; obj5.anchorMax = Vector2.one; Vector2 offsetMin = (obj5.offsetMax = Vector2.zero); obj5.offsetMin = offsetMin; Text obj6 = val4.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); Instance._discordLogoCoroutine = ((MonoBehaviour)Instance).StartCoroutine(Instance.LoadDiscordLogoDelayed()); HideVanillaDiscordButton(fejd); DiscordPanelVisibility.RegisterPanel(_discordPanel); DiscordPanelVisibility.Refresh(fejd, "Discord panel created"); ManualLogSource? modLog = ModLog; if (modLog != null) { modLog.LogInfo((object)("Discord panel ready (canvas=" + ((Object)val).name + ").")); } } } catch (Exception ex) { ManualLogSource? modLog2 = ModLog; if (modLog2 != null) { modLog2.LogError((object)("Discord panel failed: " + ex)); } } } private static void EnsureDiscordJoinButton(FejdStartup fejd, float discordContentWidth = 300f) { //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Expected O, but got Unknown //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_00b5: Expected O, but got Unknown if ((Object)(object)_discordPanel == (Object)null) { return; } if ((Object)(object)_discordJoinButton != (Object)null) { _discordJoinButton.SetActive(true); return; } Button val = FindBestStartMenuButton(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, fejd); } } private static GameObject CreateFallbackDiscordButton(Transform parent, float width, FejdStartup fejd) { //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, FindBestMenuTmpText(fejd)); return val; } private static void OnDiscordJoinClicked() { if (!DiscordPanelVisibility.TryOpenInviteUrl()) { LogWarn("Discord invite URL is missing or invalid in config."); } } private IEnumerator LoadDiscordLogoDelayed() { yield return 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 && (Object)(object)_discordLogoImage != (Object)null) { _discordLogoImage.sprite = val; _discordLogoImage.overrideSprite = val; _discordLogoImage.type = (Type)0; ((Graphic)_discordLogoImage).color = Color.white; ((Graphic)_discordLogoImage).material = null; foreach (Transform item in ((Component)_discordLogoImage).transform) { Transform val2 = item; if (((Object)val2).name == "DiscordGlyph") { ((Component)val2).gameObject.SetActive(false); } } } _discordLogoCoroutine = null; } internal static void PauseMenuMusicForCinematic() { AudioSource val = Instance?._menuMusicSource; if (!((Object)(object)val == (Object)null) && val.isPlaying) { val.Pause(); Instance._menuMusicPausedForCinematic = true; LogInfo("Menu music paused for cinematic."); } } internal static float GetVanillaMusicGain() { try { if ((Object)(object)MusicMan.instance != (Object)null) { return Mathf.Clamp01(MusicMan.m_masterMusicVolume); } float num = PlatformPrefs.GetFloat("MusicVolume", 1f); float num2 = PlatformPrefs.GetFloat("MasterVolume", 1f); return Mathf.Clamp01(num * num2); } catch { return 1f; } } internal static void ApplyMenuMusicVolume() { CustomMainMenuPlugin instance = Instance; AudioSource val = instance?._menuMusicSource; if (!((Object)(object)val == (Object)null)) { val.volume = 0.72f * GetVanillaMusicGain() * instance._menuMusicFadeFactor; } } internal static void ResumeMenuMusicAfterCinematic() { if (MainMenuActive && !MenuMusicFadeCommittedForSession) { CustomMainMenuPlugin instance = Instance; if ((Object)(object)instance?._menuMusicSource != (Object)null && instance._menuMusicPausedForCinematic) { instance._menuMusicSource.UnPause(); instance._menuMusicPausedForCinematic = false; ApplyMenuMusicVolume(); LogInfo("Menu music resumed after cinematic."); } else if (HasCustomMenuMusic()) { StartMenuMusic(); } } } internal static void OnVanillaStartupMusicReset() { if (!MainMenuActive || MenuMusicFadeCommittedForSession || CinematicsManager.IsStartedPlaying()) { return; } SilenceMusicManForMainMenu(); if (!HasCustomMenuMusic()) { return; } AudioSource val = Instance?._menuMusicSource; if ((Object)(object)val != (Object)null && (Object)(object)val.clip != (Object)null) { ApplyMenuMusicVolume(); if (!val.isPlaying) { val.Play(); } } else { StartMenuMusic(); } } internal static void StartMenuMusic() { if (!((Object)(object)Instance == (Object)null) && !CinematicsManager.IsStartedPlaying()) { if (Instance._menuMusicStartCoroutine != null) { ((MonoBehaviour)Instance).StopCoroutine(Instance._menuMusicStartCoroutine); Instance._menuMusicStartCoroutine = null; } Instance._menuMusicStartCoroutine = ((MonoBehaviour)Instance).StartCoroutine(LoadAndPlayMusic()); } } private static IEnumerator LoadAndPlayMusic() { yield return (object)new WaitForSecondsRealtime(0.1f); string musicPath = GetMusicPath(); if (!File.Exists(musicPath)) { LogWarn("mainmenu.ogg missing: " + musicPath); yield break; } string absoluteUri = new Uri(Path.GetFullPath(musicPath)).AbsoluteUri; UnityWebRequest req = UnityWebRequestMultimedia.GetAudioClip(absoluteUri, (AudioType)14); try { yield return req.SendWebRequest(); AudioClip content = DownloadHandlerAudioClip.GetContent(req); if ((Object)(object)content == (Object)null || (Object)(object)Instance == (Object)null) { LogWarn("Music failed: " + req.error); yield break; } EnsureMenuMusicAudio(Instance); AudioSource menuMusicSource = Instance._menuMusicSource; menuMusicSource.clip = content; menuMusicSource.loop = true; AttachMenuMusicMixer(menuMusicSource); Instance._menuMusicFadeFactor = 1f; ApplyMenuMusicVolume(); if (!menuMusicSource.isPlaying) { menuMusicSource.Play(); } LogInfo("Menu music playing."); if ((Object)(object)Instance != (Object)null) { Instance._menuMusicStartCoroutine = null; } } finally { ((IDisposable)req)?.Dispose(); } } private static void EnsureMenuMusicAudio(CustomMainMenuPlugin plug) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Expected O, but got Unknown if (!((Object)(object)plug._menuMusicSource != (Object)null) || !((Object)(object)plug._menuMusicGo != (Object)null)) { plug._menuMusicGo = new GameObject("CustomMainMenuMusicAudio"); Object.DontDestroyOnLoad((Object)(object)plug._menuMusicGo); plug._menuMusicSource = plug._menuMusicGo.AddComponent<AudioSource>(); plug._menuMusicSource.playOnAwake = false; plug._menuMusicSource.loop = true; plug._menuMusicSource.spatialBlend = 0f; AttachMenuMusicMixer(plug._menuMusicSource); } } private static void AttachMenuMusicMixer(AudioSource src) { try { MusicMan instance = MusicMan.instance; if (!((Object)(object)instance == (Object)null) && !((Object)(object)instance.m_musicMixer == (Object)null)) { src.outputAudioMixerGroup = instance.m_musicMixer; } } catch { } } internal static void StopMenuMusic() { CustomMainMenuPlugin instance = Instance; if ((Object)(object)instance?._menuMusicSource != (Object)null) { instance._menuMusicSource.Stop(); instance._menuMusicSource.clip = null; } if ((Object)(object)instance?._menuMusicGo != (Object)null) { Object.Destroy((Object)(object)instance._menuMusicGo); instance._menuMusicGo = null; instance._menuMusicSource = null; } if ((Object)(object)instance != (Object)null) { instance._menuMusicFadeFactor = 1f; } } internal static void FadeOutAndStopMenuMusic() { if ((Object)(object)Instance == (Object)null) { StopMenuMusic(); return; } if ((Object)(object)Instance._menuMusicSource == (Object)null || (Object)(object)Instance._menuMusicGo == (Object)null) { StopMenuMusic(); return; } if (Instance._menuMusicFadeCoroutine != null) { ((MonoBehaviour)Instance).StopCoroutine(Instance._menuMusicFadeCoroutine); Instance._menuMusicFadeCoroutine = null; } Instance._menuMusicPausedForCinematic = false; Instance._menuMusicFadeCoroutine = ((MonoBehaviour)Instance).StartCoroutine(FadeMusic(2.25f)); } private static IEnumerator FadeMusic(float seconds) { CustomMainMenuPlugin plug = Instance; if ((Object)(object)plug?._menuMusicSource == (Object)null) { StopMenuMusic(); yield break; } float start = plug._menuMusicFadeFactor; float t = 0f; while (t < seconds && (Object)(object)plug._menuMusicSource != (Object)null) { t += Time.unscaledDeltaTime; plug._menuMusicFadeFactor = Mathf.Lerp(start, 0f, Mathf.Clamp01(t / seconds)); ApplyMenuMusicVolume(); yield return null; } if ((Object)(object)plug != (Object)null) { plug._menuMusicFadeCoroutine = null; } StopMenuMusic(); LogInfo("Menu music stopped after fade."); } } 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 RegisterPanel(GameObject? panelRoot) { CustomMainMenuPlugin.SetDiscordPanelRoot(panelRoot); } internal static void EnsureReady(FejdStartup? fejd, string reason) { if (CustomMainMenuPlugin.MainMenuActive && !((Object)(object)fejd == (Object)null)) { CustomMainMenuPlugin.EnsureDiscordPanel(fejd, reason); } } internal static void Refresh(FejdStartup? fejd, string reason) { GameObject discordPanelRoot = CustomMainMenuPlugin.DiscordPanelRoot; if (!((Object)(object)discordPanelRoot == (Object)null)) { bool visible = ComputeShouldBeVisible(fejd); ApplyVisible(discordPanelRoot, visible, reason); } } internal static void Hide(string reason) { GameObject discordPanelRoot = CustomMainMenuPlugin.DiscordPanelRoot; if (!((Object)(object)discordPanelRoot == (Object)null)) { ApplyVisible(discordPanelRoot, visible: false, reason); } } internal static bool ComputeShouldBeVisible(FejdStartup? fejd) { if (!CustomMainMenuPlugin.MainMenuActive) { return false; } if (!IsGatingEnabled()) { return !IsOverlayBlockingStartMenu(fejd); } if ((Object)(object)fejd == (Object)null || (Object)(object)fejd.m_mainMenu == (Object)null) { return false; } if (IsOverlayBlockingStartMenu(fejd)) { return false; } return fejd.m_mainMenu.activeInHierarchy; } internal static bool IsOverlayBlockingStartMenu(FejdStartup? fejd) { if (IsIntroOrCinematicPlaying()) { return true; } if ((Object)(object)fejd == (Object)null) { return false; } if ((Object)(object)fejd.m_cinematicsMenuList != (Object)null && fejd.m_cinematicsMenuList.activeInHierarchy) { return true; } EulaWindow eulaWindow = fejd.m_eulaWindow; if ((Object)(object)eulaWindow != (Object)null && eulaWindow.IsWindowActive()) { return true; } return false; } internal static bool IsIntroOrCinematicPlaying() { return CinematicsManager.IsStartedPlaying(); } 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; CustomMainMenuPlugin.LogInfo(string.Format("Discord panel {0} ({1}); gating={2}, mainMenuActive={3}.", visible ? "shown" : "hidden", reason, IsGatingEnabled(), IsMainMenuRootActive(FejdStartup.instance))); } } private static bool IsMainMenuRootActive(FejdStartup? fejd) { if ((Object)(object)fejd?.m_mainMenu != (Object)null) { return fejd.m_mainMenu.activeInHierarchy; } return false; } internal static void ResetSessionState() { _lastAppliedVisible = null; } } internal static class LoadingScreens { internal struct LoadingTargetResult { internal bool IsValid; internal bool IsRaw; internal bool RestoreOnClear; internal Image? Image; internal RawImage? RawImage; internal Sprite? OriginalSprite; internal Color OriginalColor; internal int Score; internal string Path; internal string ObjectName; } private const float MaxLoadWaitSeconds = 2400f; private const int MinimumValidScore = 300; private const float LoadingFadeOutSeconds = 0.6f; private static Coroutine? _fadeCoroutine; private static Coroutine? _timeoutCoroutine; private static bool _sessionArmed; private static bool _spawnHookAttached; private static bool _fallbackScanDone; private static string? _artPath; private static LoadingTargetResult _applied; private static Sprite? _appliedSprite; internal static bool LoadingArtSessionActive => _applied.IsValid; internal static void ResetForMainMenu() { StopAllWatchers("main menu reset"); } internal static void StopAllWatchers(string reason) { if (_sessionArmed || _applied.IsValid) { LogLoading("Loading art session ended (" + reason + ")."); } StopCoroutineRef(ref _fadeCoroutine); StopCoroutineRef(ref _timeoutCoroutine); DetachSpawnHook(); ClearLoadingTarget(_applied); DestroyAppliedSprite(); _applied = default(LoadingTargetResult); _sessionArmed = false; _fallbackScanDone = false; _artPath = null; } internal static void TryStartLoadingScreenWatcher(string trigger) { if (!CustomMainMenuPlugin.EnableCustomLoadingScreens || (Object)(object)CustomMainMenuPlugin.Instance == (Object)null) { LogLoading("Loading art skipped (" + trigger + "): custom loading screens disabled or plugin missing."); } else if (CustomMainMenuPlugin.MainMenuActive) { LogLoading("Loading art deferred (" + trigger + "): still on Fejd main menu (world select / UI)."); } else if (!_sessionArmed && !_applied.IsValid) { _artPath = CustomMainMenuPlugin.GetRandomLoadingArtPath(); if (string.IsNullOrEmpty(_artPath) || !File.Exists(_artPath)) { LogLoading("Loading art skipped (" + trigger + "): no art files in the LoadingArt folder."); return; } _sessionArmed = true; _fallbackScanDone = false; AttachSpawnHook(); _timeoutCoroutine = ((MonoBehaviour)CustomMainMenuPlugin.Instance).StartCoroutine(TimeoutGuardCoroutine()); LogLoading("Loading art armed (" + trigger + "): " + _artPath); TryApplyToHud(Hud.instance, trigger); } } internal static void OnHudAwake(Hud hud) { if (_sessionArmed && !_applied.IsValid) { TryApplyToHud(hud, "Hud.Awake"); } } private static void TryApplyToHud(Hud? hud, string trigger) { if ((Object)(object)hud == (Object)null || _applied.IsValid || string.IsNullOrEmpty(_artPath)) { return; } if ((Object)(object)hud.m_loadingImage != (Object)null) { if (TryApplyHudLoadingArt(_artPath, hud.m_loadingImage, out LoadingTargetResult result, out Sprite sprite)) { _applied = result; _appliedSprite = sprite; LogLoading("Loading art applied via Hud.m_loadingImage (" + trigger + ")."); } } else { if (_fallbackScanDone) { return; } _fallbackScanDone = true; Sprite val = SpriteFromFile(_artPath); if (!((Object)(object)val == (Object)null)) { Stopwatch stopwatch = Stopwatch.StartNew(); LoadingTargetResult loadingTargetResult = FindBestConnectedWorldLoadingTarget(); stopwatch.Stop(); LogLoading($"Loading UI fallback scan ({trigger}): {stopwatch.ElapsedMilliseconds} ms, valid={loadingTargetResult.IsValid}, score={loadingTargetResult.Score}."); if (loadingTargetResult.IsValid && ApplyLoadingTargetResult(loadingTargetResult, val)) { _applied = loadingTargetResult; _appliedSprite = val; LogLoading("Loading art applied (" + trigger + "): " + loadingTargetResult.ObjectName + " @ " + loadingTargetResult.Path + "."); } else { DestroySprite(val); } } } } 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); CustomMainMenuPlugin.IsWorldFullyLoaded(); _sessionArmed = false; FadeOutAndClearLoadingTarget(_applied); } private static IEnumerator TimeoutGuardCoroutine() { yield return (object)new WaitForSecondsRealtime(2400f); _timeoutCoroutine = null; LogLoading("Loading art timeout reached; restoring vanilla loading screen."); StopAllWatchers("timeout"); } private static void StopCoroutineRef(ref Coroutine? coroutine) { if (coroutine != null) { if ((Object)(object)CustomMainMenuPlugin.Instance != (Object)null) { ((MonoBehaviour)CustomMainMenuPlugin.Instance).StopCoroutine(coroutine); } coroutine = null; } } private static void DestroyAppliedSprite() { DestroySprite(_appliedSprite); _appliedSprite = null; } private static void DestroySprite(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 LogLoading(string message) { CustomMainMenuPlugin.LogInfo(message); } private static bool TryApplyHudLoadingArt(string path, Image image, out LoadingTargetResult result, out Sprite? sprite) { //IL_0064: 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) result = default(LoadingTargetResult); sprite = null; try { sprite = SpriteFromFile(path); if ((Object)(object)sprite == (Object)null) { return false; } result = new LoadingTargetResult { IsValid = true, IsRaw = false, RestoreOnClear = true, Image = image, RawImage = null, OriginalSprite = image.sprite, OriginalColor = ((Graphic)image).color, Score = 10000, Path = GetHierarchyPath(((Component)image).transform), ObjectName = ((Object)((Component)image).gameObject).name }; if (!ApplyLoadingTargetResult(result, sprite)) { DestroySprite(sprite); sprite = null; result = default(LoadingTargetResult); return false; } return true; } catch (Exception ex) { CustomMainMenuPlugin.LogWarn("Hud loading art failed: " + ex.Message); DestroySprite(sprite); sprite = null; result = default(LoadingTargetResult); return false; } } 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 FadeOutAndClearLoadingTarget(LoadingTargetResult result) { if (!result.IsValid || (Object)(object)CustomMainMenuPlugin.Instance == (Object)null) { ClearLoadingTarget(result); DestroyAppliedSprite(); _applied = default(LoadingTargetResult); } else { StopCoroutineRef(ref _fadeCoroutine); _fadeCoroutine = ((MonoBehaviour)CustomMainMenuPlugin.Instance).StartCoroutine(LoadingFadeCoroutine(result)); } } private static IEnumerator LoadingFadeCoroutine(LoadingTargetResult result) { float elapsed = 0f; float startAlpha = 1f; if (result.IsRaw && (Object)(object)result.RawImage != (Object)null) { startAlpha = ((Graphic)result.RawImage).canvasRenderer.GetAlpha(); } else if ((Object)(object)result.Image != (Object)null) { startAlpha = ((Graphic)result.Image).canvasRenderer.GetAlpha(); } while (elapsed < 0.6f) { elapsed += Time.unscaledDeltaTime; float num = Mathf.Clamp01(elapsed / 0.6f); float a = Mathf.Lerp(startAlpha, 0f, num); TryApplyAlpha(result, a); yield return null; } ClearLoadingTarget(result); DestroyAppliedSprite(); _applied = default(LoadingTargetResult); _fadeCoroutine = null; } private static void TryApplyAlpha(LoadingTargetResult result, float a) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) if (result.IsValid) { Color color = default(Color); ((Color)(ref color))..ctor(1f, 1f, 1f, Mathf.Clamp01(a)); if (result.IsRaw && (Object)(object)result.RawImage != (Object)null) { ((Graphic)result.RawImage).color = color; ((Graphic)result.RawImage).SetAllDirty(); } else if (!result.IsRaw && (Object)(object)result.Image != (Object)null) { ((Graphic)result.Image).color = color; ((Graphic)result.Image).SetAllDirty(); } } } internal static LoadingTargetResult FindBestConnectedWorldLoadingTarget() { LoadingTargetResult result = new LoadingTargetResult { IsValid = false, IsRaw = false, Image = null, RawImage = null, Score = int.MinValue, Path = string.Empty, ObjectName = string.Empty }; Image[] array = Resources.FindObjectsOfTypeAll<Image>(); foreach (Image val in array) { if ((Object)(object)val != (Object)null && (Object)(object)((Component)val).gameObject != (Object)null) { int num = CalculateConnectedWorldLoadingImageScore(val); if (num > result.Score) { result.IsValid = num >= 300; result.IsRaw = false; result.Image = val; result.RawImage = null; result.Score = num; result.Path = GetHierarchyPath(((Component)val).transform); result.ObjectName = ((Object)((Component)val).gameObject).name; } } } RawImage[] array2 = Resources.FindObjectsOfTypeAll<RawImage>(); foreach (RawImage val2 in array2) { if ((Object)(object)val2 != (Object)null && (Object)(object)((Component)val2).gameObject != (Object)null) { int num2 = CalculateConnectedWorldLoadingRawImageScore(val2); if (num2 > result.Score) { result.IsValid = num2 >= 300; result.IsRaw = true; result.Image = null; result.RawImage = val2; result.Score = num2; result.Path = GetHierarchyPath(((Component)val2).transform); result.ObjectName = ((Object)((Component)val2).gameObject).name; } } } return result; } private static bool ApplyLoadingTargetResult(LoadingTargetResult result, Sprite customSprite) { if (!result.IsValid || (Object)(object)customSprite == (Object)null) { return false; } if (result.IsRaw) { if ((Object)(object)result.RawImage == (Object)null) { return false; } ApplyLoadingRawImage(result.RawImage, customSprite); } else { if ((Object)(object)result.Image == (Object)null) { return false; } ApplyLoadingImage(result.Image, customSprite); } return true; } 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 void ApplyLoadingRawImage(RawImage target, Sprite customSprite) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) target.texture = (Texture)(object)customSprite.texture; ((Behaviour)target).enabled = true; ((Graphic)target).material = null; ((Graphic)target).color = Color.white; StretchToFullscreen(((Graphic)target).rectTransform); ((Graphic)target).SetAllDirty(); } private static void ClearLoadingTarget(LoadingTargetResult result) { //IL_00c8: 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_0145: Unknown result type (might be due to invalid IL or missing references) if (result.IsValid) { if (result.RestoreOnClear && !result.IsRaw && (Object)(object)result.Image != (Object)null && (Object)(object)((Component)result.Image).gameObject != (Object)null) { result.Image.sprite = result.OriginalSprite; result.Image.overrideSprite = result.OriginalSprite; ((Graphic)result.Image).color = result.OriginalColor; ((Graphic)result.Image).SetAllDirty(); } else if (result.IsRaw && (Object)(object)result.RawImage != (Object)null && (Object)(object)((Component)result.RawImage).gameObject != (Object)null) { result.RawImage.texture = null; ((Graphic)result.RawImage).color = new Color(1f, 1f, 1f, 0f); ((Behaviour)result.RawImage).enabled = false; ((Graphic)result.RawImage).SetAllDirty(); } else if (!result.IsRaw && (Object)(object)result.Image != (Object)null && (Object)(object)((Component)result.Image).gameObject != (Object)null) { result.Image.sprite = null; result.Image.overrideSprite = null; ((Graphic)result.Image).color = new Color(1f, 1f, 1f, 0f); ((Behaviour)result.Image).enabled = false; ((Graphic)result.Image).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 int CalculateConnectedWorldLoadingImageScore(Image img) { //IL_043c: Unknown result type (might be due to invalid IL or missing references) //IL_0441: Unknown result type (might be due to invalid IL or missing references) //IL_0453: Unknown result type (might be due to invalid IL or missing references) //IL_0458: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)((img != null) ? ((Component)img).gameObject : null) == (Object)null) { return int.MinValue; } string text = ((Object)((Component)img).gameObject).name ?? string.Empty; text = text.ToLowerInvariant(); string text2 = GetHierarchyPath(((Component)img).transform).ToLowerInvariant(); string text3 = (((Object)(object)img.sprite != (Object)null) ? (((Object)img.sprite).name ?? string.Empty).ToLowerInvariant() : string.Empty); if (!text2.Contains("loadinggui")) { return int.MinValue; } if (text2.Contains("popupblockingbackground") || text2.Contains("unifiedpopup") || text2.Contains("achievementpopup") || text2.Contains("achievementui") || text2.Contains("editguild") || text2.Contains("createguild") || text2.Contains("searchguild") || text2.Contains("guildmanagementui") || text2.Contains("applicationsui") || text2.Contains("noguild") || text2.Contains("discord") || text2.Contains("_console(clone)") || text2.Contains("/_console") || text2.Contains("logoutput") || text2.Contains("serveroptions") || text2.Contains("connectionfailed") || text2.Contains("managesavesmenu") || text2.Contains("removecharacterdialog") || text2.Contains("pleasewait")) { return int.MinValue; } if (!text.Contains("crosshair") && !text.Contains("healthicon") && !text.Contains("foodicon") && !text.Contains("windicon") && !text.Contains("mounticon") && !text.Contains("player_marker") && !text.Contains("ship_marker") && !text.Contains("wind_marker")) { switch (text) { case "backgroundback": case "buttonclose": case "button": case "border": case "bkg": case "bar": case "darken": case "handle": case "separator": break; default: { if (text2.Contains("/enemyhud/") || text2.Contains("/inventory_screen/") || text2.Contains("/statuseffects/") || text2.Contains("/keyhints/") || text2.Contains("/buildhud/") || text2.Contains("/shiphud/controls/") || text2.Contains("/crosshair/") || text2.Contains("/guardianpower/")) { return int.MinValue; } int num = 0; if (text == "screen") { num += 5000; } if (text2.EndsWith("/hud/screen")) { num += 5000; } if (text2.Contains("/hud/screen")) { num += 3000; } if (text2.Contains("loadinggui")) { num += 300; } if (text2.Contains("pixelfix")) { num += 40; } if (text2.Contains("/hud/")) { num += 150; } if (text2.Contains("/ingamegui/")) { num += 50; } if (text == "background") { num += 180; } if (text.Contains("background")) { num += 120; } if (text3.Contains("background")) { num += 60; } if (text.Contains("image")) { num += 20; } if (((Behaviour)img).enabled) { num += 20; } if (((Component)img).gameObject.activeInHierarchy) { num += 60; } if ((Object)(object)((Graphic)img).canvasRenderer != (Object)null && ((Graphic)img).canvasRenderer.GetAlpha() > 0.01f) { num += 40; } RectTransform rectTransform = ((Graphic)img).rectTransform; if ((Object)(object)rectTransform != (Object)null) { Rect rect = rectTransform.rect; float num2 = Mathf.Abs(((Rect)(ref rect)).width); rect = rectTransform.rect; float num3 = Mathf.Abs(((Rect)(ref rect)).height); float num4 = num2 * num3; if (num4 < 200000f) { return int.MinValue; } if (num4 > 300000f) { num += 60; } if (num4 > 500000f) { num += 120; } if (num4 > 1000000f) { num += 220; } if (num4 > 1800000f) { num += 320; } if (num2 >= (float)Screen.width * 0.7f) { num += 80; } if (num3 >= (float)Screen.height * 0.7f) { num += 80; } if (num2 >= (float)Screen.width * 0.9f) { num += 100; } if (num3 >= (float)Screen.height * 0.9f) { num += 100; } if (IsFullscreenLike(rectTransform)) { num += 400; } } return num; } } } return int.MinValue; } internal static int CalculateConnectedWorldLoadingRawImageScore(RawImage raw) { //IL_02c7: Unknown result type (might be due to invalid IL or missing references) //IL_02cc: Unknown result type (might be due to invalid IL or missing references) //IL_02dd: Unknown result type (might be due to invalid IL or missing references) //IL_02e2: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)((raw != null) ? ((Component)raw).gameObject : null) == (Object)null) { return int.MinValue; } string text = (((Object)((Component)raw).gameObject).name ?? string.Empty).ToLowerInvariant(); string text2 = GetHierarchyPath(((Component)raw).transform).ToLowerInvariant(); if (!text2.Contains("loadinggui")) { return int.MinValue; } if (text2.Contains("popupblockingbackground") || text2.Contains("unifiedpopup") || text2.Contains("achievementpopup") || text2.Contains("achievementui") || text2.Contains("editguild") || text2.Contains("createguild") || text2.Contains("searchguild") || text2.Contains("guildmanagementui") || text2.Contains("applicationsui") || text2.Contains("noguild") || text2.Contains("discord") || text2.Contains("_console(clone)") || text2.Contains("/_console") || text2.Contains("logoutput") || text2.Contains("serveroptions") || text2.Contains("connectionfailed") || text2.Contains("managesavesmenu") || text2.Contains("removecharacterdialog") || text2.Contains("pleasewait")) { return int.MinValue; } if (text2.Contains("/enemyhud/") || text2.Contains("/inventory_screen/") || text2.Contains("/statuseffects/") || text2.Contains("/keyhints/") || text2.Contains("/buildhud/") || text2.Contains("/shiphud/controls/") || text2.Contains("/crosshair/") || text2.Contains("/guardianpower/")) { return int.MinValue; } int num = 0; if (text == "screen") { num += 5000; } if (text2.EndsWith("/hud/screen")) { num += 5000; } if (text2.Contains("/hud/screen")) { num += 3000; } if (text2.Contains("loadinggui")) { num += 300; } if (text2.Contains("pixelfix")) { num += 40; } if (text2.Contains("/hud/")) { num += 150; } if (text == "background") { num += 180; } if (text.Contains("background")) { num += 120; } if (text.Contains("image")) { num += 20; } if (((Behaviour)raw).enabled) { num += 20; } if (((Component)raw).gameObject.activeInHierarchy) { num += 60; } RectTransform rectTransform = ((Graphic)raw).rectTransform; if ((Object)(object)rectTransform != (Object)null) { Rect rect = rectTransform.rect; float num2 = Mathf.Abs(((Rect)(ref rect)).width); rect = rectTransform.rect; float num3 = Mathf.Abs(((Rect)(ref rect)).height); float num4 = num2 * num3; if (num4 < 200000f) { return int.MinValue; } if (num4 > 300000f) { num += 60; } if (num4 > 500000f) { num += 120; } if (num4 > 1000000f) { num += 220; } if (num4 > 1800000f) { num += 320; } if (num2 >= (float)Screen.width * 0.7f) { num += 80; } if (num3 >= (float)Screen.height * 0.7f) { num += 80; } if (num2 >= (float)Screen.width * 0.9f) { num += 100; } if (num3 >= (float)Screen.height * 0.9f) { num += 100; } if (IsFullscreenLike(rectTransform)) { num += 400; } } return num; } 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 MenuAmbientMute { private struct MutedSource { public AudioSource Source; public float Volume; public bool Mute; } private static ConfigEntry<bool>? _muteAmbient; private static bool _resolved; private static FieldInfo? _oceanSource; private static FieldInfo? _windSource; private static FieldInfo? _ambientLoopSource; private static readonly List<MutedSource> _muted = new List<MutedSource>(); private static readonly HashSet<int> _mutedIds = new HashSet<int>(); 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(); } internal static bool ShouldMute() { if (CustomMainMenuPlugin.ModEnabled != null && CustomMainMenuPlugin.ModEnabled.Value && CustomMainMenuPlugin.MainMenuActive) { if (_muteAmbient != null) { return _muteAmbient.Value; } return true; } return false; } internal static bool TrySilenceAndSkipVanilla() { if (!ShouldMute()) { return false; } SilenceAudioManAmbientSources(); return true; } internal static void Apply() { if (!ShouldMute()) { Restore(); return; } SilenceAudioManAmbientSources(); MuteSceneAmbientOnce(); } 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 EnsureResolved() { if (!_resolved) { _resolved = true; Type? typeFromHandle = typeof(AudioMan); _oceanSource = AccessTools.Field(typeFromHandle, "m_oceanAmbientSource"); _windSource = AccessTools.Field(typeFromHandle, "m_windLoopSource"); _ambientLoopSource = AccessTools.Field(typeFromHandle, "m_ambientLoopSource"); } } private static void SilenceAudioManAmbientSources() { try { AudioMan instance = AudioMan.instance; if (!((Object)(object)instance == (Object)null)) { EnsureResolved(); object? obj = _oceanSource?.GetValue(instance); RememberAndSilence((AudioSource?)((obj is AudioSource) ? obj : null)); object? obj2 = _windSource?.GetValue(instance); RememberAndSilence((AudioSource?)((obj2 is AudioSource) ? obj2 : null)); object? obj3 = _ambientLoopSource?.GetValue(instance); RememberAndSilence((AudioSource?)((obj3 is AudioSource) ? obj3 : null)); } } catch (Exception ex) { CustomMainMenuPlugin.LogInfo("Menu ambient mute skipped: " + ex.Message); } } private static void MuteSceneAmbientOnce() { try { AudioMan instance = AudioMan.instance; AudioMixerGroup gui = (((Object)(object)instance != (Object)null) ? instance.m_guiMixer : null); AudioMixerGroup ambient = (((Object)(object)instance != (Object)null) ? instance.m_ambientMixer : null); AudioMixerGroup music = (((Object)(object)MusicMan.instance != (Object)null) ? MusicMan.instance.m_musicMixer : null); AudioSource[] array = Object.FindObjectsByType<AudioSource>((FindObjectsInactive)1, (FindObjectsSortMode)0); int num = 0; foreach (AudioSource src in array) { if (ShouldMuteSceneSource(src, gui, ambient, music) && RememberAndSilence(src)) { num++; } } if (num > 0) { CustomMainMenuPlugin.LogInfo($"Menu ambient muted ({num} scene source(s))."); } } catch (Exception ex) { 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; } if (((Object)((Component)src).gameObject).name == "CustomMainMenuMusicAudio") { 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 enum ApplyMode { None, StartSceneFallback } internal const float DefaultNightTimeFraction = 0.875f; private const float FallbackSunAngle = 45f; private static readonly Color FallbackFogColor = new Color(0.047f, 0.165f, 0.196f, 1f); private static readonly Color FallbackAmbientLight = new Color(0.191f, 0.269f, 0.36f, 1f); private static readonly Color FallbackSunColor = new Color(0.337f, 0.729f, 0.714f, 1f); private const float FallbackFogDensity = 0.08f; private const float FallbackSunIntensity = 0.05f; private const float ValheimLitSunIntensity = 0.7f; private static ApplyMode _mode; private s