using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.Audio; using UnityEngine.Networking; using YamlDotNet.Serialization; using YamlDotNet.Serialization.NamingConventions; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("GlitnirMusicZones")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("GlitnirMusicZones")] [assembly: AssemblyTitle("GlitnirMusicZones")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace GlitnirMusicZones { internal static class DefaultConfig { public const string YAML = "# ╔══════════════════════════════════════════════════════════════════╗\r\n# ║ GLITNIR MUSIC ZONES — music_config.yaml ║\r\n# ╚══════════════════════════════════════════════════════════════════╝\r\n# Use URLs diretas de MP3 (sem expiração). Recomendado: GitHub Releases\r\n# ou cdn.pixabay.com. Evite links do Discord (expiram em horas).\r\n#\r\n# Após salvar o arquivo, a config recarrega automaticamente no jogo.\r\n\r\n# ─── MENU PRINCIPAL ────────────────────────────────────────────────\r\nmenu:\r\n enabled: false\r\n volume: 0.0500\r\n url: \"https://cdn.pixabay.com/download/audio/2025/07/24/audio_9d4a9c72f8.mp3?filename=wesleysert-glitnir-valhhala-1-379314.mp3\"\r\n\r\n# ─── BIOMAS ────────────────────────────────────────────────────────\r\n# Biomas disponíveis: Meadows, BlackForest, Swamp, Mountain, Plains,\r\n# Mistlands, AshLands, DeepNorth, Ocean\r\n# mode: custom = toca a url configurada (silencia vanilla)\r\n# vanilla = deixa a música do jogo tocar normalmente\r\n# mute = silêncio total (nem mod nem vanilla)\r\nbiomes:\r\n enabled: true\r\n volume: 0.0500\r\n tracks:\r\n Meadows:\r\n enabled: true\r\n mode: custom\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/25/audio_d304cbd8a2.mp3?filename=morgnar-neve-540465.mp3\"\r\n BlackForest:\r\n enabled: true\r\n mode: custom\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/22/audio_62456d9582.mp3?filename=morgnar-mire-of-ancients-538620.mp3\"\r\n Swamp:\r\n enabled: true\r\n mode: custom\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/25/audio_47a47ff6c2.mp3?filename=morgnar-pantano-540510.mp3\"\r\n Mountain:\r\n enabled: true\r\n mode: custom\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/25/audio_e6186facfc.mp3?filename=morgnar-mist-over-runestones-540508.mp3\"\r\n Plains:\r\n enabled: true\r\n mode: custom\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/25/audio_0f67273299.mp3?filename=morgnar-planicie-540517.mp3\"\r\n Mistlands:\r\n enabled: true\r\n mode: custom\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/25/audio_e6186facfc.mp3?filename=morgnar-mist-over-runestones-540508.mp3\"\r\n AshLands:\r\n enabled: true\r\n mode: custom\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/25/audio_d97092169a.mp3?filename=morgnar-ashlands-540519.mp3\"\r\n DeepNorth:\r\n enabled: true\r\n mode: custom\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/25/audio_2fe0daf552.mp3?filename=morgnar-deepnorth-540531.mp3\"\r\n Ocean:\r\n enabled: true\r\n mode: custom\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/25/audio_2fe0daf552.mp3?filename=morgnar-deepnorth-540531.mp3\"\r\n\r\n# ─── LOCATIONS (Pontos de interesse do ZoneSystem) ─────────────────\r\n# location: nome exato da Location (ex: StartTemple, Crypt2)\r\n# radius: raio em unidades (0 = usa o radius global abaixo)\r\n# dungeon_only: true = só toca dentro da dungeon (faixa Y)\r\n# dungeon_y_min/max: faixa Y da dungeon (-1 = usa o global)\r\nlocations:\r\n enabled: false\r\n volume: 0.65\r\n radius: 90.0\r\n dungeon_y_min: 4500.0\r\n dungeon_y_max: 5500.0\r\n tracks:\r\n - enabled: false\r\n location: StartTemple\r\n radius: 60.0\r\n dungeon_only: false\r\n dungeon_y_min: -1\r\n dungeon_y_max: -1\r\n url: \"\"\r\n\r\n - enabled: false\r\n location: Vendor_BlackForest\r\n radius: 0.0\r\n dungeon_only: false\r\n dungeon_y_min: 4500.0\r\n dungeon_y_max: -1\r\n url: \"\"\r\n\r\n - enabled: false\r\n location: Crypt2\r\n radius: 90.0\r\n dungeon_only: false\r\n dungeon_y_min: 4500.0\r\n dungeon_y_max: 5500.0\r\n url: \"\"\r\n\r\n - enabled: true\r\n location: Crypt3\r\n radius: 90.0\r\n dungeon_only: true\r\n dungeon_y_min: 4500\r\n dungeon_y_max: 5500\r\n url: \"\"\r\n\r\n - enabled: false\r\n location: MountainCave01\r\n radius: 90.0\r\n dungeon_only: false\r\n dungeon_y_min: 4500\r\n dungeon_y_max: 5500\r\n url: \"\"\r\n\r\n# ─── TERRITORIES (Zonas por coordenada XZ) ─────────────────────────\r\n# Use para vilas ou regiões sem Location no ZoneSystem.\r\n# x/z: coordenadas do centro no mapa do Valheim\r\n# mode: custom = toca a url configurada (silencia vanilla)\r\n# vanilla = deixa a música do jogo tocar normalmente\r\n# mute = silêncio total (nem mod nem vanilla)\r\nterritories:\r\n enabled: true\r\n tracks:\r\n - enabled: true\r\n name: BosquePrmadico\r\n x: 25186.0\r\n z: 4411.0\r\n radius: 700.0\r\n volume: 0.0500\r\n mode: custom\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/25/audio_d304cbd8a2.mp3?filename=morgnar-neve-540465.mp3\"\r\n\r\n - enabled: true\r\n name: Vila Prado\r\n x: -245.0\r\n z: 336.0\r\n radius: 200.0\r\n volume: 0.0500\r\n mode: custom\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/22/audio_e0d6b4179f.mp3?filename=morgnar-harbor-hearth-village-1-538622.mp3\"\r\n\r\n - enabled: true\r\n name: Skjalfhame\r\n x: -37714.0\r\n z: 9799.0\r\n radius: 100.0\r\n volume: 0.0500\r\n mode: custom\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/25/audio_6ab79d7f51.mp3?filename=morgnar-vila-540514.mp3\"\r\n\r\n - enabled: true\r\n name: Hrotghar\r\n x: 3337.0\r\n z: -6096.0\r\n radius: 400.0\r\n volume: 0.0500\r\n mode: custom\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/25/audio_6ab79d7f51.mp3?filename=morgnar-vila-540514.mp3\"\r\n\r\n - enabled: true\r\n name: Winterhold\r\n x: 17938.0\r\n z: 14584.0\r\n radius: 70.0\r\n volume: 0.0500\r\n mode: custom\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/25/audio_6ab79d7f51.mp3?filename=morgnar-vila-540514.mp3\"\r\n\r\n - enabled: true\r\n name: Vila da Floresta Negra\r\n x: -1997.0\r\n z: -5333.0\r\n radius: 50.0\r\n volume: 0.0500\r\n mode: custom\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/25/audio_6ab79d7f51.mp3?filename=morgnar-vila-540514.mp3\"\r\n\r\n - enabled: true\r\n name: Vila do Pantano\r\n x: 4917.0\r\n z: -2571.0\r\n radius: 200.0\r\n volume: 0.0500\r\n mode: custom\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/22/audio_e0d6b4179f.mp3?filename=morgnar-harbor-hearth-village-1-538622.mp3\"\r\n\r\n - enabled: true\r\n name: Vila da Montanha\r\n x: 6508.0\r\n z: 3735.0\r\n radius: 400.0\r\n volume: 0.0500\r\n mode: custom\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/22/audio_e0d6b4179f.mp3?filename=morgnar-harbor-hearth-village-1-538622.mp3\"\r\n\r\n - enabled: true\r\n name: Varghard - Reino dos Humanos\r\n x: 13695.0\r\n z: 19496.0\r\n radius: 400.0\r\n volume: 0.0500\r\n mode: mute\r\n url: \"\"\r\n\r\n - enabled: true\r\n name: Eryndel - Reino dos Elfos\r\n x: 21474.0\r\n z: 12080.0\r\n radius: 400.0\r\n volume: 0.0500\r\n mode: mute\r\n url: \"\"\r\n\r\n - enabled: true\r\n name: Drakthar - Reino dos Orcs\r\n x: -7346.0\r\n z: 20523.0\r\n radius: 400.0\r\n volume: 0.0500\r\n mode: mute\r\n url: \"\"\r\n\r\n - enabled: true\r\n name: Coliseu\r\n x: 1745.0\r\n z: 21713.0\r\n radius: 500.0\r\n volume: 0.0500\r\n mode: mute\r\n url: \"\"\r\n\r\n - enabled: true\r\n name: Brumello - Reino dos Halflings\r\n x: -15671.0\r\n z: 15840.0\r\n radius: 500.0\r\n volume: 0.0500\r\n mode: mute\r\n url: \"\"\r\n\r\n - enabled: true\r\n name: Cabaré do Sjar\r\n x: 14691.0\r\n z: 17973.0\r\n radius: 50.0\r\n volume: 0.0500\r\n mode: mute\r\n url: \"\"\r\n\r\n - enabled: true\r\n name: Forte Falkreath\r\n x: -22106.0\r\n z: 9622.0\r\n radius: 100.0\r\n volume: 0.0500\r\n mode: custom\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/25/audio_c2d6bad590.mp3?filename=morgnar-falkreath-citadel-541114.mp3\"\r\n\r\n - enabled: false\r\n name: Parkour\r\n x: 6508.0\r\n z: 3735.0\r\n radius: 400.0\r\n volume: 0.0500\r\n mode: custom\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/25/audio_4d4bef9969.mp3?filename=morgnar-mead-hall-waltz-541115.mp3\"\r\n\r\n - enabled: true\r\n name: Jorrvaskr\r\n x: 11411.0\r\n z: 19191.0\r\n radius: 70.0\r\n volume: 0.0500\r\n mode: custom\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/22/audio_a9a5df53e2.mp3?filename=morgnar-harbor-hearth-village-538619.mp3\"\r\n\r\n - enabled: true\r\n name: Calindel\r\n x: 8590.0\r\n z: 20394.0\r\n radius: 70.0\r\n volume: 0.0500\r\n mode: custom\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/22/audio_a9a5df53e2.mp3?filename=morgnar-harbor-hearth-village-538619.mp3\"\r\n\r\n# ─── BOSSES ────────────────────────────────────────────────────────\r\n# Prefab name do boss sem '(Clone)'. Toca enquanto o boss estiver vivo no raio.\r\n# Bosses vanilla: Eikthyr, gd_king, Bonemass, Dragon, GoblinKing, SeekerQueen, Fader\r\nbosses:\r\n enabled: true\r\n volume: 0.0500\r\n radius: 120.0\r\n tracks:\r\n Eikthyr:\r\n enabled: true\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/22/audio_1a619af2ce.mp3?filename=morgnar-stag-of-thunder-538616.mp3\"\r\n gd_king:\r\n enabled: true\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/22/audio_1a619af2ce.mp3?filename=morgnar-stag-of-thunder-538616.mp3\"\r\n Bonemass:\r\n enabled: true\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/22/audio_1a619af2ce.mp3?filename=morgnar-stag-of-thunder-538616.mp3\"\r\n Dragon:\r\n enabled: true\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/22/audio_1a619af2ce.mp3?filename=morgnar-stag-of-thunder-538616.mp3\"\r\n GoblinKing:\r\n enabled: true\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/22/audio_1a619af2ce.mp3?filename=morgnar-stag-of-thunder-538616.mp3\"\r\n SeekerQueen:\r\n enabled: true\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/22/audio_1a619af2ce.mp3?filename=morgnar-stag-of-thunder-538616.mp3\"\r\n Fader:\r\n enabled: true\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/22/audio_1a619af2ce.mp3?filename=morgnar-stag-of-thunder-538616.mp3\"\r\n brutoGLIT:\r\n enabled: true\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/22/audio_1a619af2ce.mp3?filename=morgnar-stag-of-thunder-538616.mp3\"\r\n\r\n# ─── CRIATURAS ─────────────────────────────────────────────────────\r\n# Toca enquanto houver ao menos uma criatura viva no raio.\r\ncreatures:\r\n enabled: true\r\n volume: 0.0500\r\n radius: 30.0\r\n tracks:\r\n Troll:\r\n enabled: false\r\n url: \"\"\r\n GoblinBrute:\r\n enabled: false\r\n url: \"\"\r\n BlobTar:\r\n enabled: false\r\n url: \"\"\r\n Seeker:\r\n enabled: false\r\n url: \"\"\r\n Leech:\r\n enabled: false\r\n url: \"\"\r\n"; } internal static class MusicManPatches { private static readonly FieldRef _musicSourceRef = AccessTools.FieldRefAccess("m_musicSource"); private static float _lastKnownVolume = 1f; private static AudioSource MusicManSource { get { if ((Object)(object)MusicMan.instance == (Object)null) { return null; } try { return _musicSourceRef.Invoke(MusicMan.instance); } catch { return null; } } } public static float RealMusicVolume => _lastKnownVolume; public static AudioMixerGroup GetMusicGroup() { AudioSource musicManSource = MusicManSource; return (musicManSource != null) ? musicManSource.outputAudioMixerGroup : null; } internal static void SetInitialVolume(float v) { if (v > 0f) { _lastKnownVolume = v; } } private static void PrefixUpdate() { float masterMusicVolume = MusicMan.m_masterMusicVolume; if (masterMusicVolume > 0f) { _lastKnownVolume = masterMusicVolume; } } private static void PostfixUpdate() { if (Plugin.VanillaMuted) { MusicMan.m_masterMusicVolume = 0f; AudioSource musicManSource = MusicManSource; if ((Object)(object)musicManSource != (Object)null && musicManSource.isPlaying) { musicManSource.Stop(); } } } private static bool PrefixStartMusic() { if (!Plugin.VanillaMuted) { return true; } AudioSource musicManSource = MusicManSource; if (musicManSource != null) { musicManSource.Stop(); } return false; } private static bool PrefixQueue() { return !Plugin.VanillaMuted; } public static void ApplyAll(Harmony harmony) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected O, but got Unknown //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Expected O, but got Unknown //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Expected O, but got Unknown Type typeFromHandle = typeof(MusicManPatches); HarmonyMethod val = new HarmonyMethod(typeFromHandle.GetMethod("PrefixStartMusic", BindingFlags.Static | BindingFlags.NonPublic)); HarmonyMethod val2 = new HarmonyMethod(typeFromHandle.GetMethod("PrefixQueue", BindingFlags.Static | BindingFlags.NonPublic)); HarmonyMethod val3 = new HarmonyMethod(typeFromHandle.GetMethod("PrefixUpdate", BindingFlags.Static | BindingFlags.NonPublic)); HarmonyMethod val4 = new HarmonyMethod(typeFromHandle.GetMethod("PostfixUpdate", BindingFlags.Static | BindingFlags.NonPublic)); MethodInfo methodInfo = null; MethodInfo[] methods = typeof(MusicMan).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo2 in methods) { if (methodInfo2.Name == "StartMusic") { harmony.Patch((MethodBase)methodInfo2, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } else if (methodInfo2.Name == "Queue") { harmony.Patch((MethodBase)methodInfo2, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } else if (methodInfo2.Name == "Update") { methodInfo = methodInfo2; } } if (methodInfo != null) { harmony.Patch((MethodBase)methodInfo, val3, val4, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } } public class YamlConfig { public YamlMenu menu { get; set; } = new YamlMenu(); public YamlBiomes biomes { get; set; } = new YamlBiomes(); public YamlLocations locations { get; set; } = new YamlLocations(); public YamlTerritories territories { get; set; } = new YamlTerritories(); public YamlBosses bosses { get; set; } = new YamlBosses(); public YamlCreatures creatures { get; set; } = new YamlCreatures(); } public class YamlMenu { public bool enabled { get; set; } = true; public float volume { get; set; } = 0.3f; public string url { get; set; } = ""; } public class YamlBiomes { public bool enabled { get; set; } = true; public float volume { get; set; } = 0.6f; public Dictionary tracks { get; set; } = new Dictionary(); } public class YamlLocations { public bool enabled { get; set; } = true; public float volume { get; set; } = 0.65f; public float radius { get; set; } = 90f; public float dungeon_y_min { get; set; } = 4500f; public float dungeon_y_max { get; set; } = 5500f; public List tracks { get; set; } = new List(); } public class YamlTerritories { public bool enabled { get; set; } = true; public List tracks { get; set; } = new List(); } public class YamlBosses { public bool enabled { get; set; } = true; public float volume { get; set; } = 0.7f; public float radius { get; set; } = 120f; public Dictionary tracks { get; set; } = new Dictionary(); } public class YamlCreatures { public bool enabled { get; set; } = true; public float volume { get; set; } = 0.55f; public float radius { get; set; } = 30f; public Dictionary tracks { get; set; } = new Dictionary(); } public class YamlTrack { public bool enabled { get; set; } = true; public string mode { get; set; } = "custom"; public string url { get; set; } = ""; } public class YamlLocationTrack { public bool enabled { get; set; } = true; public string location { get; set; } = ""; public float radius { get; set; } = 0f; public bool dungeon_only { get; set; } = false; public float dungeon_y_min { get; set; } = -1f; public float dungeon_y_max { get; set; } = -1f; public string url { get; set; } = ""; } public class YamlTerritoryTrack { public bool enabled { get; set; } = true; public string name { get; set; } = ""; public float x { get; set; } = 0f; public float z { get; set; } = 0f; public float radius { get; set; } = 60f; public float volume { get; set; } = 0.65f; public string mode { get; set; } = "custom"; public string url { get; set; } = ""; } public enum TerritoryMode { Custom, Vanilla, Mute } [BepInPlugin("glitnir.musiczones", "Glitnir Music Zones", "1.0.0")] public class Plugin : BaseUnityPlugin { private enum MusicState { None, Menu, Biome, Location, Territory, Boss, Creature } internal class LocationEntry { public string location; public float radius; public float dungeonYMin; public float dungeonYMax; public bool dungeonOnly; public bool enabled; public string url; public bool IsActive => enabled && !string.IsNullOrWhiteSpace(url) && !string.IsNullOrWhiteSpace(location); } internal class TerritoryEntry { public string name; public float x; public float z; public float radius; public float volume; public bool enabled; public TerritoryMode mode; public string url; public bool IsActive => enabled && (mode != TerritoryMode.Custom || !string.IsNullOrWhiteSpace(url)); } internal class MusicEntry { public bool enabled; public string url; public TerritoryMode mode; public bool IsActive => enabled && (mode != TerritoryMode.Custom || !string.IsNullOrWhiteSpace(url)); public MusicEntry(bool enabled, string url, TerritoryMode mode = TerritoryMode.Custom) { this.enabled = enabled; this.url = url ?? ""; this.mode = mode; } } private static GameObject musicObject; private static AudioSource audioSource; private string currentClipUrl = ""; private readonly Dictionary clipCache = new Dictionary(); private readonly HashSet preloadingUrls = new HashSet(); private const int MaxCachedClips = 12; private readonly Queue _cacheInsertionOrder = new Queue(); private Coroutine fadeCoroutine = null; private bool isFading = false; private float fadeDuration = 1f; private FileSystemWatcher configWatcher; private bool reloadPending = false; private float _reloadCooldown = 0f; private DateTime _watcherEventTime = DateTime.MinValue; private const float WatcherSettleDelay = 0.3f; private Harmony harmony; private MusicEntry menuEntry; private float menuVolume = 0.3f; private bool menuEnabled = true; private bool biomesEnabled = true; private float biomeVolume = 0.6f; private Dictionary biomeMap = new Dictionary(); private bool locEnabled = true; private float locVolume = 0.65f; private float locRadius = 90f; private float locDungeonYMin = 4500f; private float locDungeonYMax = 5500f; private List locList = new List(); private bool terrEnabled = true; private List terrList = new List(); private bool bossEnabled = true; private float bossVolume = 0.7f; private float bossRadius = 120f; private Dictionary bossMap = new Dictionary(); private bool creatEnabled = true; private float creatVolume = 0.55f; private float creatRadius = 30f; private Dictionary creatMap = new Dictionary(); private MusicState currentState = MusicState.None; private string currentUrl = ""; private float currentCfgVolume = 1f; private AudioClip menuClip = null; private bool menuClipReady = false; private bool worldLoaded = false; private float checkInterval = 2f; private float checkTimer = 0f; private Vector3 _lastCheckPos = Vector3.zero; private readonly Dictionary _locPosCache = new Dictionary(); private float _locCacheTimer = 0f; private const float PreloadAheadMultiplier = 1.5f; private readonly List _charBuffer = new List(); public static bool VanillaMuted { get; private set; } private string ConfigPath => Path.Combine(Paths.ConfigPath, "glitnir.musiczones", "music_config.yaml"); private float FinalVolume(float cfgVolume) { return cfgVolume * MusicManPatches.RealMusicVolume; } private void MuteVanilla() { if (!VanillaMuted) { VanillaMuted = true; ((BaseUnityPlugin)this).Logger.LogInfo((object)"[Glitnir] Vanilla silenciada."); } } private void UnmuteVanilla() { if (VanillaMuted) { VanillaMuted = false; ((BaseUnityPlugin)this).Logger.LogInfo((object)"[Glitnir] Vanilla restaurada."); } } private void Awake() { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown ((BaseUnityPlugin)this).Logger.LogInfo((object)"[Glitnir Music Zones] v1.0.0 iniciando..."); harmony = new Harmony("glitnir.musiczones"); try { MusicManPatches.ApplyAll(harmony); ((BaseUnityPlugin)this).Logger.LogInfo((object)"[Glitnir] Patches Harmony aplicados com sucesso."); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("[Glitnir] Falha ao aplicar patches: " + ex.Message)); } EnsureDefaultConfig(); LoadConfig(); CreateMusicPlayer(); StartConfigWatcher(); if ((Object)(object)MusicMan.instance != (Object)null && MusicMan.m_masterMusicVolume > 0f) { MusicManPatches.SetInitialVolume(MusicMan.m_masterMusicVolume); } ((MonoBehaviour)this).StartCoroutine(PreloadMenuMusic()); } private void OnDestroy() { Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } configWatcher?.Dispose(); UnmuteVanilla(); ClearCache(); } private void Update() { if (_reloadCooldown > 0f) { _reloadCooldown -= Time.deltaTime; } if (reloadPending && _reloadCooldown <= 0f && (DateTime.UtcNow - _watcherEventTime).TotalSeconds >= 0.30000001192092896) { reloadPending = false; _reloadCooldown = 1f; ((BaseUnityPlugin)this).Logger.LogInfo((object)"[Glitnir] Config alterada — recarregando..."); LoadConfig(); currentUrl = ""; currentState = MusicState.None; } if ((Object)(object)audioSource != (Object)null && audioSource.isPlaying && !isFading) { audioSource.volume = FinalVolume(currentCfgVolume); } bool flag = (Object)(object)ZNet.instance != (Object)null; if (flag && !worldLoaded && (Object)(object)Player.m_localPlayer != (Object)null) { worldLoaded = true; ((BaseUnityPlugin)this).Logger.LogInfo((object)"[Glitnir] Mundo carregado — parando música do menu."); StopOurMusic(); menuClip = null; menuClipReady = false; } if (!flag && worldLoaded) { worldLoaded = false; UnmuteVanilla(); ClearCache(); _locPosCache.Clear(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"[Glitnir] Voltou ao menu."); ((MonoBehaviour)this).StartCoroutine(PreloadMenuMusic()); } if (!flag) { if (!menuEnabled || menuEntry == null || !menuEntry.IsActive) { if (VanillaMuted) { UnmuteVanilla(); } } else if (menuClipReady && currentState != MusicState.Menu) { PlayCachedClip(menuClip, FinalVolume(menuVolume), MusicState.Menu, menuEntry.url, menuVolume); } } else if (worldLoaded) { checkTimer -= Time.deltaTime; if (!(checkTimer > 0f)) { checkTimer = checkInterval; UpdateInGameMusic(); } } } private void UpdateInGameMusic() { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: 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_0025: 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_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_02d2: Unknown result type (might be due to invalid IL or missing references) //IL_031b: Unknown result type (might be due to invalid IL or missing references) //IL_04a7: Unknown result type (might be due to invalid IL or missing references) //IL_04ad: Unknown result type (might be due to invalid IL or missing references) //IL_04b9: Unknown result type (might be due to invalid IL or missing references) //IL_04be: Unknown result type (might be due to invalid IL or missing references) //IL_04c6: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return; } Vector3 position = ((Component)localPlayer).transform.position; float num = Vector3.Distance(position, _lastCheckPos); checkInterval = ((num < 3f) ? 4f : 2f); _lastCheckPos = position; _locCacheTimer += checkInterval; if (_locCacheTimer > 30f) { _locPosCache.Clear(); _locCacheTimer = 0f; } TryPreloadNearby(position); if (bossEnabled || creatEnabled) { string text = null; string text2 = null; float num2 = Math.Max(bossRadius, creatRadius); _charBuffer.Clear(); foreach (Character allCharacter in Character.GetAllCharacters()) { if (!((Object)(object)allCharacter == (Object)null) && !allCharacter.IsDead()) { if (Vector3.Distance(position, ((Component)allCharacter).transform.position) <= num2) { _charBuffer.Add(allCharacter); } if (_charBuffer.Count >= 64) { break; } } } foreach (Character item in _charBuffer) { float num3 = Vector3.Distance(position, ((Component)item).transform.position); string key = ((Object)item).name.Replace("(Clone)", "").Trim(); if (bossEnabled && text == null && num3 <= bossRadius && bossMap.TryGetValue(key, out var value) && value.IsActive) { text = value.url; } if (creatEnabled && text2 == null && num3 <= creatRadius && creatMap.TryGetValue(key, out var value2) && value2.IsActive) { text2 = value2.url; } if (text != null && text2 != null) { break; } } if (text != null) { TryPreload(text); SwitchMusic(text, bossVolume, MusicState.Boss); return; } if (text2 != null) { TryPreload(text2); SwitchMusic(text2, creatVolume, MusicState.Creature); return; } } if (locEnabled) { string locationMusic = GetLocationMusic(position); if (!string.IsNullOrEmpty(locationMusic)) { TryPreload(locationMusic); SwitchMusic(locationMusic, locVolume, MusicState.Location); return; } } if (terrEnabled && GetTerritoryMatch(position, out var url, out var volume, out var mode)) { switch (mode) { case TerritoryMode.Custom: TryPreload(url); SwitchMusic(url, volume, MusicState.Territory); return; case TerritoryMode.Mute: if (currentState != MusicState.Territory || currentUrl != "__mute__") { ((BaseUnityPlugin)this).Logger.LogInfo((object)"[Glitnir] [Territory/Mute] Silenciando tudo."); MuteVanilla(); if (fadeCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(fadeCoroutine); } isFading = false; if (audioSource.isPlaying) { fadeCoroutine = ((MonoBehaviour)this).StartCoroutine(FadeOutAndStop()); } currentState = MusicState.Territory; currentUrl = "__mute__"; } return; case TerritoryMode.Vanilla: if (currentState != MusicState.Territory || currentUrl != "__vanilla__") { ((BaseUnityPlugin)this).Logger.LogInfo((object)"[Glitnir] [Territory/Vanilla] Restaurando música vanilla."); if (fadeCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(fadeCoroutine); } isFading = false; fadeCoroutine = ((MonoBehaviour)this).StartCoroutine(FadeOutAndStop(andUnmute: true)); currentState = MusicState.Territory; currentUrl = "__vanilla__"; } return; } } if (biomesEnabled) { Biome key2 = (Biome)((WorldGenerator.instance != null) ? ((int)WorldGenerator.instance.GetBiome(position.x, position.z, 0.02f, false)) : 0); if (biomeMap.TryGetValue(key2, out var value3) && value3.IsActive) { switch (value3.mode) { case TerritoryMode.Custom: TryPreload(value3.url); SwitchMusic(value3.url, biomeVolume, MusicState.Biome); return; case TerritoryMode.Mute: if (currentState != MusicState.Biome || currentUrl != "__mute__biome__") { ((BaseUnityPlugin)this).Logger.LogInfo((object)"[Glitnir] [Biome/Mute] Silenciando tudo."); MuteVanilla(); if (fadeCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(fadeCoroutine); } isFading = false; if (audioSource.isPlaying) { fadeCoroutine = ((MonoBehaviour)this).StartCoroutine(FadeOutAndStop()); } currentState = MusicState.Biome; currentUrl = "__mute__biome__"; } return; case TerritoryMode.Vanilla: if (currentState != MusicState.Biome || currentUrl != "__vanilla__biome__") { ((BaseUnityPlugin)this).Logger.LogInfo((object)"[Glitnir] [Biome/Vanilla] Restaurando música vanilla."); if (fadeCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(fadeCoroutine); } isFading = false; fadeCoroutine = ((MonoBehaviour)this).StartCoroutine(FadeOutAndStop(andUnmute: true)); currentState = MusicState.Biome; currentUrl = "__vanilla__biome__"; } return; } } } if (currentState != MusicState.None) { if (fadeCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(fadeCoroutine); } fadeCoroutine = ((MonoBehaviour)this).StartCoroutine(FadeOutAndStop(andUnmute: true)); currentState = MusicState.None; currentUrl = ""; ((BaseUnityPlugin)this).Logger.LogInfo((object)"[Glitnir] Sem zona ativa — vanilla tocando."); } } private void TryPreloadNearby(Vector3 pos) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: 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_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_013f: 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_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_0173: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Unknown result type (might be due to invalid IL or missing references) //IL_0255: Unknown result type (might be due to invalid IL or missing references) //IL_0260: Unknown result type (might be due to invalid IL or missing references) //IL_0266: Unknown result type (might be due to invalid IL or missing references) //IL_026b: Unknown result type (might be due to invalid IL or missing references) //IL_0277: 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) foreach (TerritoryEntry terr in terrList) { if (terr.IsActive && terr.mode == TerritoryMode.Custom) { float num = Vector3.Distance(new Vector3(pos.x, 0f, pos.z), new Vector3(terr.x, 0f, terr.z)); if (num <= terr.radius * 1.5f) { TryPreload(terr.url); } } } if (biomesEnabled && WorldGenerator.instance != null) { float num2 = 64f; Vector3[] array = (Vector3[])(object)new Vector3[5] { pos, new Vector3(pos.x + num2, pos.y, pos.z), new Vector3(pos.x - num2, pos.y, pos.z), new Vector3(pos.x, pos.y, pos.z + num2), new Vector3(pos.x, pos.y, pos.z - num2) }; Vector3[] array2 = array; foreach (Vector3 val in array2) { Biome biome = WorldGenerator.instance.GetBiome(val.x, val.z, 0.02f, false); if (biomeMap.TryGetValue(biome, out var value) && value.IsActive) { TryPreload(value.url); } } } if (locEnabled) { foreach (LocationEntry loc in locList) { if (loc.IsActive && _locPosCache.TryGetValue(loc.location, out var value2)) { float num3 = ((loc.radius > 0f) ? loc.radius : locRadius); float num4 = Vector3.Distance(new Vector3(pos.x, 0f, pos.z), new Vector3(value2.x, 0f, value2.z)); if (num4 <= num3 * 1.5f) { TryPreload(loc.url); } } } } if ((!bossEnabled && !creatEnabled) || _charBuffer.Count <= 0) { return; } foreach (Character item in _charBuffer) { if (!((Object)(object)item == (Object)null)) { string key = ((Object)item).name.Replace("(Clone)", "").Trim(); if (bossEnabled && bossMap.TryGetValue(key, out var value3) && value3.IsActive) { TryPreload(value3.url); } if (creatEnabled && creatMap.TryGetValue(key, out var value4) && value4.IsActive) { TryPreload(value4.url); } } } } private string GetLocationMusic(Vector3 origin) { //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZoneSystem.instance == (Object)null) { return null; } LocationInstance val = default(LocationInstance); foreach (LocationEntry loc in locList) { if (!loc.IsActive) { continue; } float num = ((loc.dungeonYMin >= 0f) ? loc.dungeonYMin : locDungeonYMin); float num2 = ((loc.dungeonYMax >= 0f) ? loc.dungeonYMax : locDungeonYMax); bool flag = origin.y >= num && origin.y <= num2; if (loc.dungeonOnly) { if (!flag) { continue; } return loc.url; } if (!_locPosCache.TryGetValue(loc.location, out var value)) { if (!ZoneSystem.instance.FindClosestLocation(loc.location, origin, ref val)) { continue; } value = val.m_position; _locPosCache[loc.location] = value; } float num3 = ((loc.radius > 0f) ? loc.radius : locRadius); float num4 = (float)Math.Sqrt(Math.Pow(origin.x - value.x, 2.0) + Math.Pow(origin.z - value.z, 2.0)); if (!(num4 <= num3)) { continue; } return loc.url; } return null; } private bool GetTerritoryMatch(Vector3 origin, out string url, out float volume, out TerritoryMode mode) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0039: 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) foreach (TerritoryEntry terr in terrList) { if (terr.IsActive) { float num = Vector3.Distance(new Vector3(origin.x, 0f, origin.z), new Vector3(terr.x, 0f, terr.z)); if (num <= terr.radius) { url = terr.url; volume = terr.volume; mode = terr.mode; return true; } } } url = null; volume = 0f; mode = TerritoryMode.Custom; return false; } private void TryPreload(string url) { if (!string.IsNullOrWhiteSpace(url) && !clipCache.ContainsKey(url) && !preloadingUrls.Contains(url)) { ((MonoBehaviour)this).StartCoroutine(PreloadClip(url)); } } private void AddToCache(string url, AudioClip clip) { if ((Object)(object)clip == (Object)null) { clipCache[url] = null; return; } bool flag = !clipCache.ContainsKey(url); clipCache[url] = clip; if (flag) { _cacheInsertionOrder.Enqueue(url); } int num = 0; int count = _cacheInsertionOrder.Count; while (_cacheInsertionOrder.Count > 12 && num < count) { num++; string text = _cacheInsertionOrder.Peek(); if (text == currentClipUrl) { break; } _cacheInsertionOrder.Dequeue(); if (clipCache.TryGetValue(text, out var value) && (Object)(object)value != (Object)null) { Object.Destroy((Object)(object)value); ((BaseUnityPlugin)this).Logger.LogInfo((object)$"[Glitnir] Cache LRU: liberando '{text.Split(new char[1] { '/' }).Last()}' ({clipCache.Count - 1} clips)"); } clipCache.Remove(text); } } private IEnumerator PreloadClip(string url) { preloadingUrls.Add(url); ((BaseUnityPlugin)this).Logger.LogInfo((object)("[Glitnir] Pré-carregando: " + url)); UnityWebRequest req = UnityWebRequestMultimedia.GetAudioClip(url, (AudioType)13); try { ((DownloadHandlerAudioClip)req.downloadHandler).compressed = false; req.timeout = 20; yield return req.SendWebRequest(); if ((int)req.result != 1) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[Glitnir] Falha no pré-carregamento (URL inválida/expirada?): " + req.error + " — " + url)); clipCache[url] = null; preloadingUrls.Remove(url); yield break; } AudioClip clip = DownloadHandlerAudioClip.GetContent(req); preloadingUrls.Remove(url); if ((Object)(object)clip != (Object)null) { AddToCache(url, clip); ((BaseUnityPlugin)this).Logger.LogInfo((object)$"[Glitnir] Cache: {((Object)clip).name} ({clipCache.Count} clips em RAM)"); } } finally { ((IDisposable)req)?.Dispose(); } } private void SwitchMusic(string url, float cfgVolume, MusicState state) { if (!(currentUrl == url)) { ((BaseUnityPlugin)this).Logger.LogInfo((object)$"[Glitnir] [{state}] → {url}"); currentState = state; currentUrl = url; currentCfgVolume = cfgVolume; MuteVanilla(); if (fadeCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(fadeCoroutine); } isFading = false; if (clipCache.TryGetValue(url, out var value) && (Object)(object)value != (Object)null) { fadeCoroutine = ((MonoBehaviour)this).StartCoroutine(FadeSwitchCached(value, url, cfgVolume)); } else { fadeCoroutine = ((MonoBehaviour)this).StartCoroutine(FadeSwitch(url, cfgVolume)); } } } private IEnumerator FadeSwitchCached(AudioClip nextClip, string url, float cfgVolume) { isFading = true; try { float startVol = audioSource.volume; float t = 0f; while (t < fadeDuration) { t += Time.deltaTime; audioSource.volume = Mathf.Lerp(startVol, 0f, t / fadeDuration); yield return null; } audioSource.Stop(); currentClipUrl = url; audioSource.clip = nextClip; audioSource.volume = 0f; audioSource.Play(); t = 0f; while (t < fadeDuration) { t += Time.deltaTime; audioSource.volume = Mathf.Lerp(0f, FinalVolume(cfgVolume), t / fadeDuration); yield return null; } audioSource.volume = FinalVolume(cfgVolume); ((BaseUnityPlugin)this).Logger.LogInfo((object)$"[Glitnir] Tocando do cache (vol={audioSource.volume:F2})."); } finally { isFading = false; } } private IEnumerator FadeSwitch(string url, float cfgVolume) { isFading = true; try { float waitLimit = fadeDuration + 15f; float waited = 0f; float startVol = audioSource.volume; float t = 0f; while (t < fadeDuration || (preloadingUrls.Contains(url) && waited < waitLimit)) { float dt = Time.deltaTime; t += dt; waited += dt; if (t <= fadeDuration) { audioSource.volume = Mathf.Lerp(startVol, 0f, t / fadeDuration); } else { audioSource.volume = 0f; } if (!preloadingUrls.Contains(url) && t >= fadeDuration) { break; } yield return null; } audioSource.Stop(); ReleaseCurrentClip(); if (clipCache.TryGetValue(url, out var cached) && (Object)(object)cached != (Object)null) { currentClipUrl = url; audioSource.clip = cached; audioSource.volume = 0f; audioSource.Play(); t = 0f; while (t < fadeDuration) { t += Time.deltaTime; audioSource.volume = Mathf.Lerp(0f, FinalVolume(cfgVolume), t / fadeDuration); yield return null; } audioSource.volume = FinalVolume(cfgVolume); ((BaseUnityPlugin)this).Logger.LogInfo((object)$"[Glitnir] Tocando do cache (vol={audioSource.volume:F2})."); } else { yield return ((MonoBehaviour)this).StartCoroutine(LoadAndFadeIn(url, cfgVolume)); } } finally { isFading = false; } } private IEnumerator FadeOutAndStop(bool andUnmute = false) { isFading = true; try { float startVol = audioSource.volume; float t = 0f; while (t < fadeDuration) { t += Time.deltaTime; audioSource.volume = Mathf.Lerp(startVol, 0f, t / fadeDuration); yield return null; } audioSource.Stop(); ReleaseCurrentClip(); if (andUnmute) { UnmuteVanilla(); } } finally { isFading = false; } } private IEnumerator LoadAndFadeIn(string url, float cfgVolume) { if (string.IsNullOrWhiteSpace(url)) { yield break; } if (clipCache.TryGetValue(url, out var preloaded) && (Object)(object)preloaded != (Object)null) { currentClipUrl = url; audioSource.clip = preloaded; audioSource.volume = 0f; audioSource.Play(); float t2 = 0f; while (t2 < fadeDuration) { t2 += Time.deltaTime; audioSource.volume = Mathf.Lerp(0f, FinalVolume(cfgVolume), t2 / fadeDuration); yield return null; } audioSource.volume = FinalVolume(cfgVolume); ((BaseUnityPlugin)this).Logger.LogInfo((object)$"[Glitnir] Tocando (vol={audioSource.volume:F2})."); yield break; } UnityWebRequest req = UnityWebRequestMultimedia.GetAudioClip(url, (AudioType)13); try { ((DownloadHandlerAudioClip)req.downloadHandler).compressed = false; req.timeout = 15; yield return req.SendWebRequest(); if ((int)req.result != 1) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[Glitnir] Falha ao carregar (URL inválida/expirada?): " + req.error + " — " + url)); clipCache[url] = null; yield break; } AudioClip clip = DownloadHandlerAudioClip.GetContent(req); if ((Object)(object)clip == (Object)null || currentUrl != url) { if ((Object)(object)clip != (Object)null) { Object.Destroy((Object)(object)clip); } yield break; } AddToCache(url, clip); preloadingUrls.Remove(url); ReleaseCurrentClip(); audioSource.clip = clip; audioSource.volume = 0f; audioSource.Play(); currentClipUrl = url; float t3 = 0f; while (t3 < fadeDuration) { t3 += Time.deltaTime; audioSource.volume = Mathf.Lerp(0f, FinalVolume(cfgVolume), t3 / fadeDuration); yield return null; } audioSource.volume = FinalVolume(cfgVolume); ((BaseUnityPlugin)this).Logger.LogInfo((object)$"[Glitnir] Tocando (vol={audioSource.volume:F2})."); } finally { ((IDisposable)req)?.Dispose(); } } private void PlayCachedClip(AudioClip clip, float finalVolume, MusicState state, string url, float cfgVolume) { if (!(currentUrl == url)) { currentState = state; currentUrl = url; currentClipUrl = url; currentCfgVolume = cfgVolume; MuteVanilla(); audioSource.Stop(); audioSource.clip = clip; audioSource.volume = finalVolume; audioSource.Play(); } } private IEnumerator PreloadMenuMusic() { if (menuEntry == null || !menuEnabled || !menuEntry.IsActive) { yield break; } if (clipCache.TryGetValue(menuEntry.url, out var cached)) { if ((Object)(object)cached != (Object)null) { menuClip = cached; menuClipReady = true; ((BaseUnityPlugin)this).Logger.LogInfo((object)"[Glitnir] Menu: usando clip do cache."); } yield break; } ((BaseUnityPlugin)this).Logger.LogInfo((object)"[Glitnir] Pré-carregando música do menu..."); UnityWebRequest req = UnityWebRequestMultimedia.GetAudioClip(menuEntry.url, (AudioType)13); try { ((DownloadHandlerAudioClip)req.downloadHandler).compressed = false; req.timeout = 15; yield return req.SendWebRequest(); if ((int)req.result != 1) { ((BaseUnityPlugin)this).Logger.LogError((object)("[Glitnir] Falha no menu: " + req.error)); clipCache[menuEntry.url] = null; yield break; } AudioClip clip = DownloadHandlerAudioClip.GetContent(req); if ((Object)(object)clip != (Object)null) { AddToCache(menuEntry.url, clip); menuClip = clip; menuClipReady = true; } ((BaseUnityPlugin)this).Logger.LogInfo((object)("[Glitnir] Menu " + (menuClipReady ? "pronto." : "clip nulo!"))); } finally { ((IDisposable)req)?.Dispose(); } } private void StopOurMusic() { if (fadeCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(fadeCoroutine); } isFading = false; audioSource.Stop(); ReleaseCurrentClip(); currentState = MusicState.None; currentUrl = ""; } private void ReleaseCurrentClip() { if (!string.IsNullOrEmpty(currentClipUrl)) { if (!clipCache.ContainsKey(currentClipUrl) && (Object)(object)audioSource.clip != (Object)null) { Object.Destroy((Object)(object)audioSource.clip); } audioSource.clip = null; currentClipUrl = ""; } } private void ClearCache() { menuClip = null; menuClipReady = false; foreach (AudioClip value in clipCache.Values) { if ((Object)(object)value != (Object)null) { Object.Destroy((Object)(object)value); } } clipCache.Clear(); preloadingUrls.Clear(); _cacheInsertionOrder.Clear(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"[Glitnir] Cache de clips liberado."); } private void CreateMusicPlayer() { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown if (!((Object)(object)musicObject != (Object)null) || !((Object)(object)audioSource != (Object)null)) { if ((Object)(object)musicObject == (Object)null) { musicObject = new GameObject("Glitnir Music Player"); Object.DontDestroyOnLoad((Object)(object)musicObject); } audioSource = musicObject.GetComponent() ?? musicObject.AddComponent(); audioSource.loop = true; audioSource.spatialBlend = 0f; audioSource.volume = 1f; TryConnectToMusicMixer(); } } private void TryConnectToMusicMixer() { AudioMixerGroup musicGroup = MusicManPatches.GetMusicGroup(); if ((Object)(object)musicGroup != (Object)null) { audioSource.outputAudioMixerGroup = musicGroup; ((BaseUnityPlugin)this).Logger.LogInfo((object)"[Glitnir] AudioSource conectado ao mixer de música do jogo."); } else { ((MonoBehaviour)this).StartCoroutine(RetryConnectMixer()); } } private IEnumerator RetryConnectMixer() { float elapsed = 0f; while (elapsed < 10f) { yield return (object)new WaitForSeconds(0.5f); elapsed += 0.5f; AudioMixerGroup group = MusicManPatches.GetMusicGroup(); if ((Object)(object)group != (Object)null) { audioSource.outputAudioMixerGroup = group; ((BaseUnityPlugin)this).Logger.LogInfo((object)"[Glitnir] AudioSource conectado ao mixer de música do jogo (retry)."); yield break; } } ((BaseUnityPlugin)this).Logger.LogError((object)"[Glitnir] Não foi possível conectar ao AudioMixerGroup do MusicMan — o slider 'Volume da música' pode não afetar o volume do mod!"); } private void StartConfigWatcher() { try { string directoryName = Path.GetDirectoryName(ConfigPath); string fileName = Path.GetFileName(ConfigPath); if (Directory.Exists(directoryName)) { configWatcher = new FileSystemWatcher(directoryName, fileName) { NotifyFilter = NotifyFilters.LastWrite, EnableRaisingEvents = true }; configWatcher.Changed += delegate { _watcherEventTime = DateTime.UtcNow; reloadPending = true; }; ((BaseUnityPlugin)this).Logger.LogInfo((object)"[Glitnir] Watcher de config ativo."); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[Glitnir] Watcher não iniciado: " + ex.Message)); } } private void LoadConfig() { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Expected O, but got Unknown //IL_016f: Unknown result type (might be due to invalid IL or missing references) if (!File.Exists(ConfigPath)) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[Glitnir] Config não encontrada: " + ConfigPath)); return; } try { string text = File.ReadAllText(ConfigPath); IDeserializer val = ((BuilderSkeleton)new DeserializerBuilder()).WithNamingConvention(UnderscoredNamingConvention.Instance).IgnoreUnmatchedProperties().Build(); YamlConfig yamlConfig = val.Deserialize(text); if (yamlConfig == null) { throw new Exception("YAML resultou em objeto nulo."); } bool enabled = yamlConfig.menu.enabled; float volume = yamlConfig.menu.volume; MusicEntry musicEntry = new MusicEntry(enabled: true, yamlConfig.menu.url); bool enabled2 = yamlConfig.biomes.enabled; float volume2 = yamlConfig.biomes.volume; Dictionary dictionary = new Dictionary(); foreach (KeyValuePair track in yamlConfig.biomes.tracks) { if (Enum.TryParse(track.Key, out Biome result)) { TerritoryMode mode = TerritoryMode.Custom; if (!string.IsNullOrWhiteSpace(track.Value.mode)) { string text2 = track.Value.mode.Trim().ToLowerInvariant(); string text3 = text2; mode = ((text3 == "vanilla") ? TerritoryMode.Vanilla : ((text3 == "mute") ? TerritoryMode.Mute : TerritoryMode.Custom)); } dictionary[result] = new MusicEntry(track.Value.enabled, track.Value.url, mode); } } bool enabled3 = yamlConfig.locations.enabled; float volume3 = yamlConfig.locations.volume; float radius = yamlConfig.locations.radius; float dungeon_y_min = yamlConfig.locations.dungeon_y_min; float dungeon_y_max = yamlConfig.locations.dungeon_y_max; List list = new List(); foreach (YamlLocationTrack track2 in yamlConfig.locations.tracks) { list.Add(new LocationEntry { location = track2.location, radius = track2.radius, dungeonYMin = track2.dungeon_y_min, dungeonYMax = track2.dungeon_y_max, dungeonOnly = track2.dungeon_only, enabled = track2.enabled, url = track2.url }); } bool enabled4 = yamlConfig.territories.enabled; List list2 = new List(); foreach (YamlTerritoryTrack track3 in yamlConfig.territories.tracks) { TerritoryMode mode2 = TerritoryMode.Custom; if (!string.IsNullOrWhiteSpace(track3.mode)) { string text4 = track3.mode.Trim().ToLowerInvariant(); string text5 = text4; mode2 = ((text5 == "vanilla") ? TerritoryMode.Vanilla : ((text5 == "mute") ? TerritoryMode.Mute : TerritoryMode.Custom)); } list2.Add(new TerritoryEntry { name = track3.name, x = track3.x, z = track3.z, radius = track3.radius, volume = track3.volume, enabled = track3.enabled, mode = mode2, url = track3.url }); } bool enabled5 = yamlConfig.bosses.enabled; float volume4 = yamlConfig.bosses.volume; float radius2 = yamlConfig.bosses.radius; Dictionary dictionary2 = new Dictionary(); foreach (KeyValuePair track4 in yamlConfig.bosses.tracks) { dictionary2[track4.Key] = new MusicEntry(track4.Value.enabled, track4.Value.url); } bool enabled6 = yamlConfig.creatures.enabled; float volume5 = yamlConfig.creatures.volume; float radius3 = yamlConfig.creatures.radius; Dictionary dictionary3 = new Dictionary(); foreach (KeyValuePair track5 in yamlConfig.creatures.tracks) { dictionary3[track5.Key] = new MusicEntry(track5.Value.enabled, track5.Value.url); } ClearCache(); _locPosCache.Clear(); menuEnabled = enabled; menuVolume = volume; menuEntry = musicEntry; biomesEnabled = enabled2; biomeVolume = volume2; biomeMap = dictionary; locEnabled = enabled3; locVolume = volume3; locRadius = radius; locDungeonYMin = dungeon_y_min; locDungeonYMax = dungeon_y_max; locList = list; terrEnabled = enabled4; terrList = list2; bossEnabled = enabled5; bossVolume = volume4; bossRadius = radius2; bossMap = dictionary2; creatEnabled = enabled6; creatVolume = volume5; creatRadius = radius3; creatMap = dictionary3; ((BaseUnityPlugin)this).Logger.LogInfo((object)("[Glitnir] Config carregada: " + $"{biomeMap.Count} biomas, {locList.Count} locations, " + $"{terrList.Count} territories, {bossMap.Count} bosses, {creatMap.Count} criaturas.")); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("[Glitnir] Erro ao ler config: " + ex.Message)); ((BaseUnityPlugin)this).Logger.LogError((object)("[Glitnir] Verifique o arquivo: " + ConfigPath)); } } private void EnsureDefaultConfig() { if (!File.Exists(ConfigPath)) { Directory.CreateDirectory(Path.GetDirectoryName(ConfigPath)); File.WriteAllText(ConfigPath, "# ╔══════════════════════════════════════════════════════════════════╗\r\n# ║ GLITNIR MUSIC ZONES — music_config.yaml ║\r\n# ╚══════════════════════════════════════════════════════════════════╝\r\n# Use URLs diretas de MP3 (sem expiração). Recomendado: GitHub Releases\r\n# ou cdn.pixabay.com. Evite links do Discord (expiram em horas).\r\n#\r\n# Após salvar o arquivo, a config recarrega automaticamente no jogo.\r\n\r\n# ─── MENU PRINCIPAL ────────────────────────────────────────────────\r\nmenu:\r\n enabled: false\r\n volume: 0.0500\r\n url: \"https://cdn.pixabay.com/download/audio/2025/07/24/audio_9d4a9c72f8.mp3?filename=wesleysert-glitnir-valhhala-1-379314.mp3\"\r\n\r\n# ─── BIOMAS ────────────────────────────────────────────────────────\r\n# Biomas disponíveis: Meadows, BlackForest, Swamp, Mountain, Plains,\r\n# Mistlands, AshLands, DeepNorth, Ocean\r\n# mode: custom = toca a url configurada (silencia vanilla)\r\n# vanilla = deixa a música do jogo tocar normalmente\r\n# mute = silêncio total (nem mod nem vanilla)\r\nbiomes:\r\n enabled: true\r\n volume: 0.0500\r\n tracks:\r\n Meadows:\r\n enabled: true\r\n mode: custom\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/25/audio_d304cbd8a2.mp3?filename=morgnar-neve-540465.mp3\"\r\n BlackForest:\r\n enabled: true\r\n mode: custom\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/22/audio_62456d9582.mp3?filename=morgnar-mire-of-ancients-538620.mp3\"\r\n Swamp:\r\n enabled: true\r\n mode: custom\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/25/audio_47a47ff6c2.mp3?filename=morgnar-pantano-540510.mp3\"\r\n Mountain:\r\n enabled: true\r\n mode: custom\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/25/audio_e6186facfc.mp3?filename=morgnar-mist-over-runestones-540508.mp3\"\r\n Plains:\r\n enabled: true\r\n mode: custom\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/25/audio_0f67273299.mp3?filename=morgnar-planicie-540517.mp3\"\r\n Mistlands:\r\n enabled: true\r\n mode: custom\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/25/audio_e6186facfc.mp3?filename=morgnar-mist-over-runestones-540508.mp3\"\r\n AshLands:\r\n enabled: true\r\n mode: custom\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/25/audio_d97092169a.mp3?filename=morgnar-ashlands-540519.mp3\"\r\n DeepNorth:\r\n enabled: true\r\n mode: custom\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/25/audio_2fe0daf552.mp3?filename=morgnar-deepnorth-540531.mp3\"\r\n Ocean:\r\n enabled: true\r\n mode: custom\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/25/audio_2fe0daf552.mp3?filename=morgnar-deepnorth-540531.mp3\"\r\n\r\n# ─── LOCATIONS (Pontos de interesse do ZoneSystem) ─────────────────\r\n# location: nome exato da Location (ex: StartTemple, Crypt2)\r\n# radius: raio em unidades (0 = usa o radius global abaixo)\r\n# dungeon_only: true = só toca dentro da dungeon (faixa Y)\r\n# dungeon_y_min/max: faixa Y da dungeon (-1 = usa o global)\r\nlocations:\r\n enabled: false\r\n volume: 0.65\r\n radius: 90.0\r\n dungeon_y_min: 4500.0\r\n dungeon_y_max: 5500.0\r\n tracks:\r\n - enabled: false\r\n location: StartTemple\r\n radius: 60.0\r\n dungeon_only: false\r\n dungeon_y_min: -1\r\n dungeon_y_max: -1\r\n url: \"\"\r\n\r\n - enabled: false\r\n location: Vendor_BlackForest\r\n radius: 0.0\r\n dungeon_only: false\r\n dungeon_y_min: 4500.0\r\n dungeon_y_max: -1\r\n url: \"\"\r\n\r\n - enabled: false\r\n location: Crypt2\r\n radius: 90.0\r\n dungeon_only: false\r\n dungeon_y_min: 4500.0\r\n dungeon_y_max: 5500.0\r\n url: \"\"\r\n\r\n - enabled: true\r\n location: Crypt3\r\n radius: 90.0\r\n dungeon_only: true\r\n dungeon_y_min: 4500\r\n dungeon_y_max: 5500\r\n url: \"\"\r\n\r\n - enabled: false\r\n location: MountainCave01\r\n radius: 90.0\r\n dungeon_only: false\r\n dungeon_y_min: 4500\r\n dungeon_y_max: 5500\r\n url: \"\"\r\n\r\n# ─── TERRITORIES (Zonas por coordenada XZ) ─────────────────────────\r\n# Use para vilas ou regiões sem Location no ZoneSystem.\r\n# x/z: coordenadas do centro no mapa do Valheim\r\n# mode: custom = toca a url configurada (silencia vanilla)\r\n# vanilla = deixa a música do jogo tocar normalmente\r\n# mute = silêncio total (nem mod nem vanilla)\r\nterritories:\r\n enabled: true\r\n tracks:\r\n - enabled: true\r\n name: BosquePrmadico\r\n x: 25186.0\r\n z: 4411.0\r\n radius: 700.0\r\n volume: 0.0500\r\n mode: custom\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/25/audio_d304cbd8a2.mp3?filename=morgnar-neve-540465.mp3\"\r\n\r\n - enabled: true\r\n name: Vila Prado\r\n x: -245.0\r\n z: 336.0\r\n radius: 200.0\r\n volume: 0.0500\r\n mode: custom\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/22/audio_e0d6b4179f.mp3?filename=morgnar-harbor-hearth-village-1-538622.mp3\"\r\n\r\n - enabled: true\r\n name: Skjalfhame\r\n x: -37714.0\r\n z: 9799.0\r\n radius: 100.0\r\n volume: 0.0500\r\n mode: custom\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/25/audio_6ab79d7f51.mp3?filename=morgnar-vila-540514.mp3\"\r\n\r\n - enabled: true\r\n name: Hrotghar\r\n x: 3337.0\r\n z: -6096.0\r\n radius: 400.0\r\n volume: 0.0500\r\n mode: custom\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/25/audio_6ab79d7f51.mp3?filename=morgnar-vila-540514.mp3\"\r\n\r\n - enabled: true\r\n name: Winterhold\r\n x: 17938.0\r\n z: 14584.0\r\n radius: 70.0\r\n volume: 0.0500\r\n mode: custom\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/25/audio_6ab79d7f51.mp3?filename=morgnar-vila-540514.mp3\"\r\n\r\n - enabled: true\r\n name: Vila da Floresta Negra\r\n x: -1997.0\r\n z: -5333.0\r\n radius: 50.0\r\n volume: 0.0500\r\n mode: custom\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/25/audio_6ab79d7f51.mp3?filename=morgnar-vila-540514.mp3\"\r\n\r\n - enabled: true\r\n name: Vila do Pantano\r\n x: 4917.0\r\n z: -2571.0\r\n radius: 200.0\r\n volume: 0.0500\r\n mode: custom\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/22/audio_e0d6b4179f.mp3?filename=morgnar-harbor-hearth-village-1-538622.mp3\"\r\n\r\n - enabled: true\r\n name: Vila da Montanha\r\n x: 6508.0\r\n z: 3735.0\r\n radius: 400.0\r\n volume: 0.0500\r\n mode: custom\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/22/audio_e0d6b4179f.mp3?filename=morgnar-harbor-hearth-village-1-538622.mp3\"\r\n\r\n - enabled: true\r\n name: Varghard - Reino dos Humanos\r\n x: 13695.0\r\n z: 19496.0\r\n radius: 400.0\r\n volume: 0.0500\r\n mode: mute\r\n url: \"\"\r\n\r\n - enabled: true\r\n name: Eryndel - Reino dos Elfos\r\n x: 21474.0\r\n z: 12080.0\r\n radius: 400.0\r\n volume: 0.0500\r\n mode: mute\r\n url: \"\"\r\n\r\n - enabled: true\r\n name: Drakthar - Reino dos Orcs\r\n x: -7346.0\r\n z: 20523.0\r\n radius: 400.0\r\n volume: 0.0500\r\n mode: mute\r\n url: \"\"\r\n\r\n - enabled: true\r\n name: Coliseu\r\n x: 1745.0\r\n z: 21713.0\r\n radius: 500.0\r\n volume: 0.0500\r\n mode: mute\r\n url: \"\"\r\n\r\n - enabled: true\r\n name: Brumello - Reino dos Halflings\r\n x: -15671.0\r\n z: 15840.0\r\n radius: 500.0\r\n volume: 0.0500\r\n mode: mute\r\n url: \"\"\r\n\r\n - enabled: true\r\n name: Cabaré do Sjar\r\n x: 14691.0\r\n z: 17973.0\r\n radius: 50.0\r\n volume: 0.0500\r\n mode: mute\r\n url: \"\"\r\n\r\n - enabled: true\r\n name: Forte Falkreath\r\n x: -22106.0\r\n z: 9622.0\r\n radius: 100.0\r\n volume: 0.0500\r\n mode: custom\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/25/audio_c2d6bad590.mp3?filename=morgnar-falkreath-citadel-541114.mp3\"\r\n\r\n - enabled: false\r\n name: Parkour\r\n x: 6508.0\r\n z: 3735.0\r\n radius: 400.0\r\n volume: 0.0500\r\n mode: custom\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/25/audio_4d4bef9969.mp3?filename=morgnar-mead-hall-waltz-541115.mp3\"\r\n\r\n - enabled: true\r\n name: Jorrvaskr\r\n x: 11411.0\r\n z: 19191.0\r\n radius: 70.0\r\n volume: 0.0500\r\n mode: custom\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/22/audio_a9a5df53e2.mp3?filename=morgnar-harbor-hearth-village-538619.mp3\"\r\n\r\n - enabled: true\r\n name: Calindel\r\n x: 8590.0\r\n z: 20394.0\r\n radius: 70.0\r\n volume: 0.0500\r\n mode: custom\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/22/audio_a9a5df53e2.mp3?filename=morgnar-harbor-hearth-village-538619.mp3\"\r\n\r\n# ─── BOSSES ────────────────────────────────────────────────────────\r\n# Prefab name do boss sem '(Clone)'. Toca enquanto o boss estiver vivo no raio.\r\n# Bosses vanilla: Eikthyr, gd_king, Bonemass, Dragon, GoblinKing, SeekerQueen, Fader\r\nbosses:\r\n enabled: true\r\n volume: 0.0500\r\n radius: 120.0\r\n tracks:\r\n Eikthyr:\r\n enabled: true\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/22/audio_1a619af2ce.mp3?filename=morgnar-stag-of-thunder-538616.mp3\"\r\n gd_king:\r\n enabled: true\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/22/audio_1a619af2ce.mp3?filename=morgnar-stag-of-thunder-538616.mp3\"\r\n Bonemass:\r\n enabled: true\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/22/audio_1a619af2ce.mp3?filename=morgnar-stag-of-thunder-538616.mp3\"\r\n Dragon:\r\n enabled: true\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/22/audio_1a619af2ce.mp3?filename=morgnar-stag-of-thunder-538616.mp3\"\r\n GoblinKing:\r\n enabled: true\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/22/audio_1a619af2ce.mp3?filename=morgnar-stag-of-thunder-538616.mp3\"\r\n SeekerQueen:\r\n enabled: true\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/22/audio_1a619af2ce.mp3?filename=morgnar-stag-of-thunder-538616.mp3\"\r\n Fader:\r\n enabled: true\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/22/audio_1a619af2ce.mp3?filename=morgnar-stag-of-thunder-538616.mp3\"\r\n brutoGLIT:\r\n enabled: true\r\n url: \"https://cdn.pixabay.com/download/audio/2026/05/22/audio_1a619af2ce.mp3?filename=morgnar-stag-of-thunder-538616.mp3\"\r\n\r\n# ─── CRIATURAS ─────────────────────────────────────────────────────\r\n# Toca enquanto houver ao menos uma criatura viva no raio.\r\ncreatures:\r\n enabled: true\r\n volume: 0.0500\r\n radius: 30.0\r\n tracks:\r\n Troll:\r\n enabled: false\r\n url: \"\"\r\n GoblinBrute:\r\n enabled: false\r\n url: \"\"\r\n BlobTar:\r\n enabled: false\r\n url: \"\"\r\n Seeker:\r\n enabled: false\r\n url: \"\"\r\n Leech:\r\n enabled: false\r\n url: \"\"\r\n"); ((BaseUnityPlugin)this).Logger.LogInfo((object)("[Glitnir] Config padrão criada em: " + ConfigPath)); } } } }