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 BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using ElsaMusicMod.Audio; using ElsaMusicMod.Components; using ElsaMusicMod.Config; using ElsaMusicMod.Utils; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.Networking; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.6", FrameworkDisplayName = ".NET Framework 4.6")] [assembly: AssemblyVersion("0.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace ElsaMusicMod { public static class Constants { public const string PluginGUID = "ua.jaffyfox.elsamusicmod"; public const string PluginName = "ElsaMusicMod"; public const string PluginVersion = "0.0.2"; public const string LogPrefix = "[ElsaMusicMod]"; public const string ConfigSectionGeneral = "General"; public const string ConfigSectionAudio = "Audio"; public const string ConfigSectionDebug = "Debug"; public const string ConfigKeyEnableMod = "EnableMod"; public const string ConfigKeyEnableSoundElsa = "EnableSoundElsa"; public const string ConfigKeyEnableSoundHuntsman = "EnableSoundHuntsman"; public const string ConfigKeySoundPathElsa = "SoundPathElsa"; public const string ConfigKeySoundPathHuntsman = "SoundPathHuntsman"; public const string ConfigKeyVolume = "Volume"; public const string ConfigKeyAudioMode = "AudioMode"; public const string ConfigKeySpatialMaxDistance = "SpatialMaxDistance"; public const string ConfigKeyDebugMode = "DebugMode"; public const string ConfigKeyLanguage = "Language"; public const string ConfigKeyFadeDuration = "FadeDuration"; public const string ConfigKeySpatialMuffle = "SpatialMuffle"; public const bool DefaultEnableMod = true; public const bool DefaultEnableSoundElsa = true; public const bool DefaultEnableSoundHuntsman = true; public const string DefaultSoundPathElsa = ""; public const string DefaultSoundPathHuntsman = ""; public const float DefaultVolume = 0.8f; public const string DefaultAudioMode = "Spatial"; public const float DefaultSpatialMaxDistance = 20f; public const bool DefaultDebugMode = false; public const string DefaultLanguage = "en"; public const float DefaultFadeDuration = 0.5f; public const bool DefaultSpatialMuffle = true; public const string AudioModeGlobal = "Global"; public const string AudioModeSpatial = "Spatial"; public const string LangEnglish = "en"; public const string LangRussian = "ru"; public const string DefaultElsaSoundFile = "Sounds/elsa_theme.ogg"; public const string DefaultHuntsmanSoundFile = "Sounds/huntsman_shoot.ogg"; public const float SpatialBlend3D = 1f; public const float SpatialBlend2D = 0f; public const float SpatialMinDistance = 1f; public const float DopplerLevel = 0f; public const float LowPassCutoffClose = 22000f; public const float LowPassCutoffFar = 400f; public const float LowPassResonance = 1f; public static readonly string[] AggressiveStateNames = new string[7] { "TransformSmallToBig", "GoToPlayerBig", "GoToPlayerOverBig", "BackToNavMeshBig", "LookUnderStartBig", "LookUnderBig", "LookUnderStopBig" }; public static readonly string[] AggressiveHunterStateNames = new string[1] { "Shoot" }; } [BepInPlugin("ua.jaffyfox.elsamusicmod", "ElsaMusicMod", "0.0.2")] public class Plugin : BaseUnityPlugin { private Harmony _harmony; public static Plugin Instance { get; private set; } public static ManualLogSource Log { get; private set; } private void Awake() { //IL_01ac: Unknown result type (might be due to invalid IL or missing references) //IL_01b6: Expected O, but got Unknown if ((Object)(object)Instance != (Object)null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"[ElsaMusicMod] Duplicate initialization — ignoring."); return; } Instance = this; Log = ((BaseUnityPlugin)this).Logger; ModConfig.Init(((BaseUnityPlugin)this).Config); Loc.LogInfo("ElsaMusicMod v0.0.2 — initializing...", "ElsaMusicMod v0.0.2 — инициализация..."); Loc.LogInfo("Config loaded.", "Конфиг загружен."); Loc.LogInfo($" EnableMod: {ModConfig.EnableMod.Value}", $" EnableMod: {ModConfig.EnableMod.Value}"); Loc.LogInfo($" EnableSoundElsa: {ModConfig.EnableSoundElsa.Value}", $" EnableSoundElsa: {ModConfig.EnableSoundElsa.Value}"); Loc.LogInfo($" EnableSoundHuntsman: {ModConfig.EnableSoundHuntsman.Value}", $" EnableSoundHuntsman: {ModConfig.EnableSoundHuntsman.Value}"); Loc.LogInfo(" Language: " + ModConfig.Language.Value, " Language: " + ModConfig.Language.Value); if (!ModConfig.EnableMod.Value) { Loc.LogInfo("Mod disabled in config. No patches applied.", "Мод отключен в конфиге. Патчи не применяются."); return; } string directoryName = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location); AudioManager.Initialize(directoryName); Loc.LogDebug("Plugin folder: " + directoryName, "Папка плагина: " + directoryName); ((MonoBehaviour)this).StartCoroutine(AudioManager.LoadAudioCoroutine(ModConfig.SoundPathElsa.Value, isElsa: true)); ((MonoBehaviour)this).StartCoroutine(AudioManager.LoadAudioCoroutine(ModConfig.SoundPathHuntsman.Value, isElsa: false)); try { _harmony = new Harmony("ua.jaffyfox.elsamusicmod"); _harmony.PatchAll(); Loc.LogInfo("Harmony patches applied successfully.", "Harmony патчи успешно применены."); } catch (Exception arg) { Loc.LogError($"Harmony patching error: {arg}", $"Ошибка при применении патчей Harmony: {arg}"); Loc.LogError("Mod will not work. Game classes may have changed in an update.", "Мод не будет работать. Возможно, классы игры изменились в обновлении."); } Loc.LogInfo("ElsaMusicMod v0.0.2 — initialized!", "ElsaMusicMod v0.0.2 — успешно инициализирован!"); } } } namespace ElsaMusicMod.Utils { public static class Loc { public static bool IsRussian { get { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 if ((int)Application.systemLanguage != 30) { if ((Object)(object)Plugin.Instance != (Object)null && ModConfig.Language != null) { return ModConfig.Language.Value == "ru"; } return false; } return true; } } public static string Get(string en, string ru) { if (!IsRussian) { return en; } return ru; } public static void LogInfo(string en, string ru) { Plugin.Log.LogInfo((object)("[ElsaMusicMod] " + Get(en, ru))); } public static void LogWarning(string en, string ru) { Plugin.Log.LogWarning((object)("[ElsaMusicMod] " + Get(en, ru))); } public static void LogError(string en, string ru) { Plugin.Log.LogError((object)("[ElsaMusicMod] " + Get(en, ru))); } public static void LogDebug(string en, string ru) { if ((Object)(object)Plugin.Instance != (Object)null && ModConfig.IsDebug) { Plugin.Log.LogInfo((object)("[ElsaMusicMod] [DEBUG] " + Get(en, ru))); } } } } namespace ElsaMusicMod.Patches { [HarmonyPatch(typeof(EnemyElsa), "Update")] internal static class EnemyElsaUpdatePatch { private static readonly Dictionary _previousStates = new Dictionary(); private static readonly HashSet _aggressiveStates = new HashSet(); private static FieldInfo _currentStateField; private static bool _initialized; private static bool _initFailed; private static void Initialize() { if (_initialized) { return; } _initialized = true; try { _currentStateField = typeof(EnemyElsa).GetField("currentState", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (_currentStateField == null) { Loc.LogError("CRITICAL: Field 'currentState' not found in EnemyElsa!", "КРИТИЧЕСКАЯ ОШИБКА: Поле 'currentState' не найдено в EnemyElsa!"); _initFailed = true; return; } Type fieldType = _currentStateField.FieldType; Loc.LogInfo("Found state field: " + _currentStateField.Name + " (type: " + fieldType.Name + ")", "Найдено поле состояния: " + _currentStateField.Name + " (тип: " + fieldType.Name + ")"); if (!fieldType.IsEnum) { Loc.LogError("CRITICAL: currentState is not an enum! Type: " + fieldType.FullName, "КРИТИЧЕСКАЯ ОШИБКА: currentState не является enum! Тип: " + fieldType.FullName); _initFailed = true; return; } string[] names; if (ModConfig.IsDebug) { Loc.LogDebug("All EnemyElsa.State values:", "Все значения EnemyElsa.State:"); names = Enum.GetNames(fieldType); foreach (string text in names) { int num = (int)Enum.Parse(fieldType, text); Loc.LogDebug($" {text} = {num}", $" {text} = {num}"); } } names = Constants.AggressiveStateNames; foreach (string text2 in names) { try { int num2 = (int)Enum.Parse(fieldType, text2); _aggressiveStates.Add(num2); Loc.LogInfo($"Aggressive state registered: {text2} = {num2}", $"Агрессивное состояние зарегистрировано: {text2} = {num2}"); } catch { Loc.LogWarning("State '" + text2 + "' not found in EnemyElsa.State enum!", "Состояние '" + text2 + "' не найдено в EnemyElsa.State enum!"); } } if (_aggressiveStates.Count == 0) { Loc.LogError("CRITICAL: No aggressive states found! Music will never play.", "КРИТИЧЕСКАЯ ОШИБКА: Агрессивные состояния не найдены! Музыка не будет играть."); _initFailed = true; } else { Loc.LogInfo($"Patch initialized OK. {_aggressiveStates.Count} aggressive states registered.", $"Патч инициализирован. Зарегистрировано {_aggressiveStates.Count} агрессивных состояний."); } } catch (Exception arg) { Loc.LogError($"Patch initialization error: {arg}", $"Ошибка инициализации патча: {arg}"); _initFailed = true; } } [HarmonyPostfix] public static void Postfix(EnemyElsa __instance) { try { if (!_initialized) { Initialize(); } if (_initFailed || !ModConfig.EnableMod.Value || (Object)(object)__instance == (Object)null) { return; } int num = (int)_currentStateField.GetValue(__instance); int instanceID = ((Object)((Component)__instance).gameObject).GetInstanceID(); int value; bool flag = _previousStates.TryGetValue(instanceID, out value); _previousStates[instanceID] = num; if (flag && value == num) { return; } bool flag2 = flag && _aggressiveStates.Contains(value); bool flag3 = _aggressiveStates.Contains(num); if (ModConfig.IsDebug) { string text = (flag ? GetStateName(value) : "none"); string stateName = GetStateName(num); Loc.LogDebug($"Elsa #{instanceID} state: {text}({value}) -> {stateName}({num}) | wasAggro={flag2} isAggro={flag3}", $"Эльза #{instanceID} состояние: {text}({value}) -> {stateName}({num}) | была_агро={flag2} агро={flag3}"); } if (flag3 && !flag2) { Loc.LogDebug($"Elsa #{instanceID} entered aggressive state! Starting music...", $"Эльза #{instanceID} вошла в агрессивное состояние! Запуск музыки..."); ElsaMusicController orAddController = GetOrAddController(((Component)__instance).gameObject); if ((Object)(object)orAddController != (Object)null) { orAddController.StartMusic(); } } else if (!flag3 && flag2) { Loc.LogDebug($"Elsa #{instanceID} left aggressive state. Stopping music...", $"Эльза #{instanceID} вышла из агрессивного состояния. Остановка музыки..."); ElsaMusicController component = ((Component)__instance).gameObject.GetComponent(); if ((Object)(object)component != (Object)null) { component.StopMusic(); } } } catch (Exception ex) { Loc.LogError("Error in EnemyElsa.Update Postfix: " + ex.Message, "Ошибка в EnemyElsa.Update Postfix: " + ex.Message); } } private static string GetStateName(int stateValue) { try { if (_currentStateField != null) { return Enum.GetName(_currentStateField.FieldType, stateValue) ?? $"Unknown({stateValue})"; } } catch { } return $"Unknown({stateValue})"; } private static ElsaMusicController GetOrAddController(GameObject gameObject) { if ((Object)(object)gameObject == (Object)null) { return null; } ElsaMusicController elsaMusicController = gameObject.GetComponent(); if ((Object)(object)elsaMusicController == (Object)null) { elsaMusicController = gameObject.AddComponent(); Loc.LogDebug($"ElsaMusicController added to {((Object)gameObject).name} (ID: {((Object)gameObject).GetInstanceID()})", $"ElsaMusicController добавлен к {((Object)gameObject).name} (ID: {((Object)gameObject).GetInstanceID()})"); } return elsaMusicController; } public static void CleanupInstance(int instanceId) { _previousStates.Remove(instanceId); Loc.LogDebug($"Cleaned up state tracking for instance #{instanceId}", $"Очищено отслеживание состояния для инстанса #{instanceId}"); } } [HarmonyPatch(typeof(EnemyHunter), "Update")] internal static class EnemyHunterUpdatePatch { private static readonly Dictionary _previousStates = new Dictionary(); private static readonly HashSet _aggressiveStates = new HashSet(); private static FieldInfo _currentStateField; private static bool _initialized; private static bool _initFailed; private static void Initialize() { if (_initialized) { return; } _initialized = true; try { _currentStateField = typeof(EnemyHunter).GetField("currentState", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (_currentStateField == null) { Loc.LogError("CRITICAL: Field 'currentState' not found in EnemyHunter!", "КРИТИЧЕСКАЯ ОШИБКА: Поле 'currentState' не найдено в EnemyHunter!"); _initFailed = true; return; } Type fieldType = _currentStateField.FieldType; Loc.LogInfo("Found state field for Hunter: " + _currentStateField.Name + " (type: " + fieldType.Name + ")", "Найдено поле состояния для Охотника: " + _currentStateField.Name + " (тип: " + fieldType.Name + ")"); if (!fieldType.IsEnum) { Loc.LogError("CRITICAL: Hunter currentState is not an enum! Type: " + fieldType.FullName, "КРИТИЧЕСКАЯ ОШИБКА: currentState Охотника не является enum! Тип: " + fieldType.FullName); _initFailed = true; return; } string[] names; if (ModConfig.IsDebug) { Loc.LogDebug("All EnemyHunter.State values:", "Все значения EnemyHunter.State:"); names = Enum.GetNames(fieldType); foreach (string text in names) { int num = (int)Enum.Parse(fieldType, text); Loc.LogDebug($" {text} = {num}", $" {text} = {num}"); } } names = Constants.AggressiveHunterStateNames; foreach (string text2 in names) { try { int num2 = (int)Enum.Parse(fieldType, text2); _aggressiveStates.Add(num2); Loc.LogInfo($"Aggressive state registered for Hunter: {text2} = {num2}", $"Агрессивное состояние зарегистрировано для Охотника: {text2} = {num2}"); } catch { Loc.LogWarning("State '" + text2 + "' not found in EnemyHunter.State enum!", "Состояние '" + text2 + "' не найдено в EnemyHunter.State enum!"); } } if (_aggressiveStates.Count == 0) { Loc.LogError("CRITICAL: No aggressive states found for Hunter! Music will never play.", "КРИТИЧЕСКАЯ ОШИБКА: Агрессивные состояния не найдены для Охотника! Музыка не будет играть."); _initFailed = true; } else { Loc.LogInfo($"Hunter Patch initialized OK. {_aggressiveStates.Count} aggressive states registered.", $"Патч Охотника инициализирован. Зарегистрировано {_aggressiveStates.Count} агрессивных состояний."); } } catch (Exception arg) { Loc.LogError($"Hunter Patch initialization error: {arg}", $"Ошибка инициализации патча Охотника: {arg}"); _initFailed = true; } } [HarmonyPostfix] public static void Postfix(EnemyHunter __instance) { try { if (!_initialized) { Initialize(); } if (_initFailed || !ModConfig.EnableMod.Value || (Object)(object)__instance == (Object)null) { return; } int num = (int)_currentStateField.GetValue(__instance); int instanceID = ((Object)((Component)__instance).gameObject).GetInstanceID(); int value; bool flag = _previousStates.TryGetValue(instanceID, out value); _previousStates[instanceID] = num; if (flag && value == num) { return; } bool flag2 = flag && _aggressiveStates.Contains(value); bool flag3 = _aggressiveStates.Contains(num); if (ModConfig.IsDebug) { string text = (flag ? GetStateName(value) : "none"); string stateName = GetStateName(num); Loc.LogDebug($"Hunter #{instanceID} state: {text}({value}) -> {stateName}({num}) | wasAggro={flag2} isAggro={flag3}", $"Охотник #{instanceID} состояние: {text}({value}) -> {stateName}({num}) | была_агро={flag2} агро={flag3}"); } if (flag3 && !flag2) { Loc.LogDebug($"Hunter #{instanceID} entered aggressive state! Playing shoot sound...", $"Охотник #{instanceID} вошел в агрессивное состояние! Запуск звука выстрела..."); HuntsmanMusicController orAddController = GetOrAddController(((Component)__instance).gameObject); if ((Object)(object)orAddController != (Object)null) { orAddController.PlayShoot(); } } } catch (Exception ex) { Loc.LogError("Error in EnemyHunter.Update Postfix: " + ex.Message, "Ошибка в EnemyHunter.Update Postfix: " + ex.Message); } } private static string GetStateName(int stateValue) { try { if (_currentStateField != null) { return Enum.GetName(_currentStateField.FieldType, stateValue) ?? $"Unknown({stateValue})"; } } catch { } return $"Unknown({stateValue})"; } private static HuntsmanMusicController GetOrAddController(GameObject gameObject) { if ((Object)(object)gameObject == (Object)null) { return null; } HuntsmanMusicController huntsmanMusicController = gameObject.GetComponent(); if ((Object)(object)huntsmanMusicController == (Object)null) { huntsmanMusicController = gameObject.AddComponent(); Loc.LogDebug($"HuntsmanMusicController added to {((Object)gameObject).name} (ID: {((Object)gameObject).GetInstanceID()})", $"HuntsmanMusicController добавлен к {((Object)gameObject).name} (ID: {((Object)gameObject).GetInstanceID()})"); } return huntsmanMusicController; } public static void CleanupInstance(int instanceId) { _previousStates.Remove(instanceId); Loc.LogDebug($"Cleaned up state tracking for Hunter instance #{instanceId}", $"Очищено отслеживание состояния для инстанса Охотника #{instanceId}"); } } [HarmonyPatch(typeof(EnemyParent), "Despawn")] internal static class EnemyParentDespawnPatch { [HarmonyPrefix] public static void Prefix(EnemyParent __instance) { try { if ((Object)(object)__instance == (Object)null) { return; } ElsaMusicController componentInChildren = ((Component)__instance).gameObject.GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null) { if (componentInChildren.IsPlaying) { componentInChildren.StopMusic(); Loc.LogInfo("Elsa despawning — music stopped.", "Эльза деспавнится — музыка остановлена."); } EnemyElsaUpdatePatch.CleanupInstance(((Object)((Component)componentInChildren).gameObject).GetInstanceID()); } HuntsmanMusicController componentInChildren2 = ((Component)__instance).gameObject.GetComponentInChildren(); if ((Object)(object)componentInChildren2 != (Object)null) { EnemyHunterUpdatePatch.CleanupInstance(((Object)((Component)componentInChildren2).gameObject).GetInstanceID()); } } catch (Exception ex) { Loc.LogWarning("Error in EnemyParent.Despawn Prefix: " + ex.Message, "Ошибка в EnemyParent.Despawn Prefix: " + ex.Message); } } } } namespace ElsaMusicMod.Config { public static class ModConfig { public static ConfigEntry EnableMod { get; private set; } public static ConfigEntry EnableSoundElsa { get; private set; } public static ConfigEntry EnableSoundHuntsman { get; private set; } public static ConfigEntry SoundPathElsa { get; private set; } public static ConfigEntry SoundPathHuntsman { get; private set; } public static ConfigEntry Volume { get; private set; } public static ConfigEntry AudioMode { get; private set; } public static ConfigEntry SpatialMaxDistance { get; private set; } public static ConfigEntry DebugMode { get; private set; } public static ConfigEntry Language { get; private set; } public static ConfigEntry FadeDuration { get; private set; } public static ConfigEntry SpatialMuffle { get; private set; } public static bool IsSpatialMode => AudioMode.Value == "Spatial"; public static bool IsDebug => DebugMode.Value; public static void Init(ConfigFile config) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Expected O, but got Unknown //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Expected O, but got Unknown //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Expected O, but got Unknown //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Expected O, but got Unknown //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Expected O, but got Unknown //IL_01b2: Unknown result type (might be due to invalid IL or missing references) //IL_01bc: Expected O, but got Unknown //IL_01ea: Unknown result type (might be due to invalid IL or missing references) //IL_01f4: Expected O, but got Unknown string text = (((int)Application.systemLanguage == 30) ? "ru" : "en"); EnableMod = config.Bind("General", "EnableMod", true, "Enable or disable the mod entirely."); Language = config.Bind("General", "Language", text, new ConfigDescription("In-game language: en (English) or ru (Russian).", (AcceptableValueBase)(object)new AcceptableValueList(new string[2] { "en", "ru" }), Array.Empty())); EnableSoundElsa = config.Bind("General", "EnableSoundElsa", true, "Enable sound for Elsa."); EnableSoundHuntsman = config.Bind("General", "EnableSoundHuntsman", true, "Enable sound for Huntsman."); SoundPathElsa = config.Bind("Audio", "SoundPathElsa", "", new ConfigDescription("Absolute path to custom Elsa audio file (.ogg/.mp3). Empty = default track.", (AcceptableValueBase)null, new object[1] { "HideFromREPOConfig" })); SoundPathHuntsman = config.Bind("Audio", "SoundPathHuntsman", "", new ConfigDescription("Absolute path to custom Huntsman audio file (.ogg/.mp3). Empty = default track.", (AcceptableValueBase)null, new object[1] { "HideFromREPOConfig" })); Volume = config.Bind("Audio", "Volume", 0.8f, new ConfigDescription("Playback volume.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); AudioMode = config.Bind("Audio", "AudioMode", "Spatial", new ConfigDescription("Audio mode: Spatial — 3D, Global — 2D.", (AcceptableValueBase)(object)new AcceptableValueList(new string[2] { "Spatial", "Global" }), Array.Empty())); SpatialMaxDistance = config.Bind("Audio", "SpatialMaxDistance", 20f, new ConfigDescription("Max hearing distance in Spatial mode (units).", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 200f), Array.Empty())); FadeDuration = config.Bind("Audio", "FadeDuration", 0.5f, new ConfigDescription("Fade in/out duration in seconds.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 3f), Array.Empty())); SpatialMuffle = config.Bind("Audio", "SpatialMuffle", true, "Muffle sound at distance (low-pass filter)."); DebugMode = config.Bind("Debug", "DebugMode", false, "Enable verbose debug logging to BepInEx console."); } } } namespace ElsaMusicMod.Components { public class ElsaMusicController : MonoBehaviour { [CompilerGenerated] private sealed class d__13 : IEnumerator, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public ElsaMusicController <>4__this; public float duration; public float targetVol; public bool stopAfterFade; private float 5__2; private float 5__3; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__13(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { int num = <>1__state; ElsaMusicController elsaMusicController = <>4__this; switch (num) { default: return false; case 0: <>1__state = -1; if ((Object)(object)elsaMusicController._audioSource == (Object)null) { return false; } 5__2 = elsaMusicController._audioSource.volume; 5__3 = 0f; break; case 1: <>1__state = -1; break; } if (5__3 < duration) { 5__3 += Time.deltaTime; float num2 = Mathf.Clamp01(5__3 / duration); num2 = num2 * num2 * (3f - 2f * num2); elsaMusicController._audioSource.volume = Mathf.Lerp(5__2, targetVol, num2); <>2__current = null; <>1__state = 1; return true; } elsaMusicController._audioSource.volume = targetVol; elsaMusicController._fadeCoroutine = null; if (stopAfterFade) { elsaMusicController.StopImmediate(); } return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private AudioSource _audioSource; private AudioLowPassFilter _lowPassFilter; private bool _isPlaying; private int _instanceId; private float _targetVolume; private Coroutine _fadeCoroutine; public bool IsPlaying => _isPlaying; private void Awake() { _instanceId = ((Object)((Component)this).gameObject).GetInstanceID(); } public void StartMusic() { if (_isPlaying || !ModConfig.EnableMod.Value || !ModConfig.EnableSoundElsa.Value) { return; } if ((Object)(object)AudioManager.ElsaClip == (Object)null) { Loc.LogWarning($"Cannot start music: AudioClip not loaded. Elsa #{_instanceId}", $"Не удалось запустить музыку: AudioClip не загружен. Эльза #{_instanceId}"); return; } EnsureAudioSource(); ApplySettings(); _audioSource.clip = AudioManager.ElsaClip; _isPlaying = true; float value = ModConfig.FadeDuration.Value; if (value > 0.01f) { _audioSource.volume = 0f; _audioSource.Play(); StartFade(_targetVolume, value); } else { _audioSource.volume = _targetVolume; _audioSource.Play(); } Loc.LogInfo($">> Music STARTED for Elsa #{_instanceId} (fade: {value:F2}s)", $">> Музыка ЗАПУЩЕНА для Эльзы #{_instanceId} (фейд: {value:F2}с)"); } public void StopMusic() { if (_isPlaying) { float value = ModConfig.FadeDuration.Value; if (value > 0.01f && (Object)(object)_audioSource != (Object)null && _audioSource.isPlaying) { StartFade(0f, value, stopAfterFade: true); } else { StopImmediate(); } Loc.LogInfo($"[] Music STOPPING for Elsa #{_instanceId} (fade: {value:F2}s)", $"[] Музыка ОСТАНАВЛИВАЕТСЯ для Эльзы #{_instanceId} (фейд: {value:F2}с)"); } } private void StopImmediate() { if (_fadeCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_fadeCoroutine); _fadeCoroutine = null; } if ((Object)(object)_audioSource != (Object)null) { _audioSource.Stop(); _audioSource.clip = null; } _isPlaying = false; } private void StartFade(float targetVol, float duration, bool stopAfterFade = false) { if (_fadeCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_fadeCoroutine); } _fadeCoroutine = ((MonoBehaviour)this).StartCoroutine(FadeCoroutine(targetVol, duration, stopAfterFade)); } [IteratorStateMachine(typeof(d__13))] private IEnumerator FadeCoroutine(float targetVol, float duration, bool stopAfterFade) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__13(0) { <>4__this = this, targetVol = targetVol, duration = duration, stopAfterFade = stopAfterFade }; } private void Update() { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) if (_isPlaying && !((Object)(object)_audioSource == (Object)null) && !((Object)(object)_lowPassFilter == (Object)null) && ModConfig.SpatialMuffle.Value && ModConfig.IsSpatialMode) { AudioListener val = Object.FindObjectOfType(); if (!((Object)(object)val == (Object)null)) { float num = Vector3.Distance(((Component)this).transform.position, ((Component)val).transform.position); float value = ModConfig.SpatialMaxDistance.Value; float num2 = Mathf.Clamp01(num / value); float num3 = 1f - num2; num3 *= num3; float cutoffFrequency = Mathf.Lerp(400f, 22000f, num3); _lowPassFilter.cutoffFrequency = cutoffFrequency; } } } private void EnsureAudioSource() { if ((Object)(object)_audioSource == (Object)null) { _audioSource = ((Component)this).gameObject.AddComponent(); Loc.LogDebug($"AudioSource created on Elsa #{_instanceId}", $"AudioSource создан на Эльзе #{_instanceId}"); } if ((Object)(object)_lowPassFilter == (Object)null && ModConfig.SpatialMuffle.Value && ModConfig.IsSpatialMode) { _lowPassFilter = ((Component)this).gameObject.AddComponent(); _lowPassFilter.cutoffFrequency = 22000f; _lowPassFilter.lowpassResonanceQ = 1f; Loc.LogDebug($"AudioLowPassFilter added to Elsa #{_instanceId}", $"AudioLowPassFilter добавлен к Эльзе #{_instanceId}"); } } private void ApplySettings() { if (!((Object)(object)_audioSource == (Object)null)) { _targetVolume = Mathf.Clamp01(ModConfig.Volume.Value); _audioSource.loop = true; _audioSource.playOnAwake = false; _audioSource.dopplerLevel = 0f; if (ModConfig.IsSpatialMode) { _audioSource.spatialBlend = 1f; _audioSource.minDistance = 1f; _audioSource.maxDistance = ModConfig.SpatialMaxDistance.Value; _audioSource.rolloffMode = (AudioRolloffMode)1; _audioSource.spread = 0f; } else { _audioSource.spatialBlend = 0f; } Loc.LogDebug($"AudioSource: vol={_targetVolume}, spatial={_audioSource.spatialBlend}, maxDist={_audioSource.maxDistance}, muffle={ModConfig.SpatialMuffle.Value}", $"AudioSource: громкость={_targetVolume}, 3D={_audioSource.spatialBlend}, макс_дистанция={_audioSource.maxDistance}, приглушение={ModConfig.SpatialMuffle.Value}"); } } private void OnDestroy() { if (_isPlaying) { if ((Object)(object)_audioSource != (Object)null) { _audioSource.Stop(); _audioSource.clip = null; } _isPlaying = false; Loc.LogInfo($"Elsa #{_instanceId} destroyed — music stopped.", $"Эльза #{_instanceId} уничтожена — музыка остановлена."); } } } public class HuntsmanMusicController : MonoBehaviour { private AudioSource _audioSource; private AudioLowPassFilter _lowPassFilter; private int _instanceId; private float _targetVolume; private void Awake() { _instanceId = ((Object)((Component)this).gameObject).GetInstanceID(); } public void PlayShoot() { if (ModConfig.EnableMod.Value && ModConfig.EnableSoundHuntsman.Value) { if ((Object)(object)AudioManager.HuntsmanClip == (Object)null) { Loc.LogWarning($"Cannot play sound: AudioClip not loaded. Huntsman #{_instanceId}", $"Не удалось запустить звук: AudioClip не загружен. Охотник #{_instanceId}"); return; } EnsureAudioSource(); ApplySettings(); _audioSource.volume = _targetVolume; _audioSource.PlayOneShot(AudioManager.HuntsmanClip); Loc.LogInfo($">> Sound PLAYED for Huntsman #{_instanceId}", $">> Звук ВОСПРОИЗВЕДЕН для Охотника #{_instanceId}"); } } private void Update() { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_audioSource == (Object)null) && _audioSource.isPlaying && !((Object)(object)_lowPassFilter == (Object)null) && ModConfig.SpatialMuffle.Value && ModConfig.IsSpatialMode) { AudioListener val = Object.FindObjectOfType(); if (!((Object)(object)val == (Object)null)) { float num = Vector3.Distance(((Component)this).transform.position, ((Component)val).transform.position); float value = ModConfig.SpatialMaxDistance.Value; float num2 = Mathf.Clamp01(num / value); float num3 = 1f - num2; num3 *= num3; float cutoffFrequency = Mathf.Lerp(400f, 22000f, num3); _lowPassFilter.cutoffFrequency = cutoffFrequency; } } } private void EnsureAudioSource() { if ((Object)(object)_audioSource == (Object)null) { _audioSource = ((Component)this).gameObject.AddComponent(); Loc.LogDebug($"AudioSource created on Huntsman #{_instanceId}", $"AudioSource создан на Охотнике #{_instanceId}"); } if ((Object)(object)_lowPassFilter == (Object)null && ModConfig.SpatialMuffle.Value && ModConfig.IsSpatialMode) { _lowPassFilter = ((Component)this).gameObject.AddComponent(); _lowPassFilter.cutoffFrequency = 22000f; _lowPassFilter.lowpassResonanceQ = 1f; Loc.LogDebug($"AudioLowPassFilter added to Huntsman #{_instanceId}", $"AudioLowPassFilter добавлен к Охотнику #{_instanceId}"); } } private void ApplySettings() { if (!((Object)(object)_audioSource == (Object)null)) { _targetVolume = Mathf.Clamp01(ModConfig.Volume.Value); _audioSource.loop = false; _audioSource.playOnAwake = false; _audioSource.dopplerLevel = 0f; if (ModConfig.IsSpatialMode) { _audioSource.spatialBlend = 1f; _audioSource.minDistance = 1f; _audioSource.maxDistance = ModConfig.SpatialMaxDistance.Value; _audioSource.rolloffMode = (AudioRolloffMode)1; _audioSource.spread = 0f; } else { _audioSource.spatialBlend = 0f; } } } } } namespace ElsaMusicMod.Audio { public static class AudioManager { [CompilerGenerated] private sealed class d__18 : IEnumerator, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public bool isElsa; public string customPath; private string 5__2; private string 5__3; private string 5__4; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__18(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { 5__2 = null; 5__3 = null; 5__4 = null; <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; 5__2 = (isElsa ? "Elsa" : "Huntsman"); 5__3 = (isElsa ? "Sounds/elsa_theme.ogg" : "Sounds/huntsman_shoot.ogg"); if (!string.IsNullOrEmpty(customPath)) { Loc.LogInfo("Attempting to load custom sound for " + 5__2 + ": " + customPath, "Попытка загрузки кастомного звука для " + 5__2 + ": " + customPath); if (File.Exists(customPath)) { <>2__current = LoadClipFromFile(customPath, isElsa); <>1__state = 1; return true; } Loc.LogWarning("Custom sound not found: " + customPath + ". Falling back to default.", "Кастомный звук не найден: " + customPath + ". Возврат к стандартному."); } goto IL_01b5; case 1: <>1__state = -1; if ((isElsa && (Object)(object)ElsaClip != (Object)null) || (!isElsa && (Object)(object)HuntsmanClip != (Object)null)) { Loc.LogInfo("Custom sound loaded successfully for " + 5__2 + ": " + customPath, "Кастомный звук успешно загружен для " + 5__2 + ": " + customPath); if (isElsa) { IsElsaLoaded = true; } else { IsHuntsmanLoaded = true; } return false; } Loc.LogWarning("Failed to load custom sound (corrupted?): " + customPath + ". Falling back to default.", "Не удалось загрузить кастомный звук (поврежден?): " + customPath + ". Возврат к стандартному."); goto IL_01b5; case 2: { <>1__state = -1; AudioClip val = (isElsa ? ElsaClip : HuntsmanClip); if ((Object)(object)val != (Object)null) { Loc.LogInfo($"Default sound loaded successfully for {5__2}. Duration: {val.length:F1}s", $"Стандартный звук успешно загружен для {5__2}. Длительность: {val.length:F1}с"); if (isElsa) { IsElsaLoaded = true; } else { IsHuntsmanLoaded = true; } } else { Loc.LogError("Failed to load default sound: " + 5__4, "Не удалось загрузить стандартный звук: " + 5__4); } break; } IL_01b5: 5__4 = Path.Combine(_pluginFolderPath, 5__3); Loc.LogInfo("Loading default sound for " + 5__2 + ": " + 5__4, "Загрузка стандартного звука для " + 5__2 + ": " + 5__4); if (File.Exists(5__4)) { <>2__current = LoadClipFromFile(5__4, isElsa); <>1__state = 2; return true; } Loc.LogError("Default sound file not found: " + 5__4 + ". Place " + 5__3 + " in plugin folder.", "Файл стандартного звука не найден: " + 5__4 + ". Поместите " + 5__3 + " в папку плагина."); break; } return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class d__19 : IEnumerator, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public string filePath; public bool isElsa; private UnityWebRequest 5__2; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__19(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || num == 1) { try { } finally { <>m__Finally1(); } } 5__2 = null; <>1__state = -2; } private bool MoveNext() { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: 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_0103: Invalid comparison between Unknown and I4 //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Invalid comparison between Unknown and I4 //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Invalid comparison between Unknown and I4 //IL_01d8: Unknown result type (might be due to invalid IL or missing references) //IL_01de: Invalid comparison between Unknown and I4 //IL_02bf: Unknown result type (might be due to invalid IL or missing references) //IL_02f0: Unknown result type (might be due to invalid IL or missing references) try { switch (<>1__state) { default: return false; case 0: { <>1__state = -1; AudioType audioType = GetAudioType(filePath); if ((int)audioType == 0) { Loc.LogWarning("Unsupported audio format: " + filePath + ". Supported: .ogg, .mp3, .wav", "Неподдерживаемый формат аудио: " + filePath + ". Поддерживаются: .ogg, .mp3, .wav"); return false; } string text = "file:///" + filePath.Replace('\\', '/'); Loc.LogDebug($"Loading audio from URI: {text} (type: {audioType})", $"Загрузка аудио по URI: {text} (тип: {audioType})"); 5__2 = UnityWebRequestMultimedia.GetAudioClip(text, audioType); <>1__state = -3; ((DownloadHandlerAudioClip)5__2.downloadHandler).streamAudio = false; <>2__current = 5__2.SendWebRequest(); <>1__state = 1; return true; } case 1: <>1__state = -3; if ((int)5__2.result == 2 || (int)5__2.result == 3 || (int)5__2.result == 4) { Loc.LogWarning("Audio load error: " + 5__2.error + " (" + filePath + ")", "Ошибка загрузки аудио: " + 5__2.error + " (" + filePath + ")"); if (isElsa) { ElsaClip = null; } else { HuntsmanClip = null; } } else { try { AudioClip content = DownloadHandlerAudioClip.GetContent(5__2); if ((Object)(object)content != (Object)null && (int)content.loadState == 2) { ((Object)content).name = Path.GetFileNameWithoutExtension(filePath); if (isElsa) { ElsaClip = content; } else { HuntsmanClip = content; } Loc.LogDebug($"AudioClip OK: name={((Object)content).name}, duration={content.length:F1}s, channels={content.channels}, freq={content.frequency}Hz", $"AudioClip ОК: имя={((Object)content).name}, длина={content.length:F1}с, каналы={content.channels}, част={content.frequency}Гц"); } else { Loc.LogWarning($"AudioClip loaded but invalid: {filePath} (loadState: {((content != null) ? new AudioDataLoadState?(content.loadState) : null)})", $"AudioClip загружен, но недействителен: {filePath} (статус: {((content != null) ? new AudioDataLoadState?(content.loadState) : null)})"); if (isElsa) { ElsaClip = null; } else { HuntsmanClip = null; } } } catch (Exception ex) { Loc.LogError("Exception processing audio: " + ex.Message, "Исключение при обработке аудио: " + ex.Message); if (isElsa) { ElsaClip = null; } else { HuntsmanClip = null; } } } <>m__Finally1(); 5__2 = null; return false; } } catch { //try-fault ((IDisposable)this).Dispose(); throw; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; if (5__2 != null) { ((IDisposable)5__2).Dispose(); } } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private static string _pluginFolderPath; public static AudioClip ElsaClip { get; private set; } public static AudioClip HuntsmanClip { get; private set; } public static bool IsElsaLoaded { get; private set; } public static bool IsHuntsmanLoaded { get; private set; } public static void Initialize(string pluginFolderPath) { _pluginFolderPath = pluginFolderPath; IsElsaLoaded = false; IsHuntsmanLoaded = false; ElsaClip = null; HuntsmanClip = null; } [IteratorStateMachine(typeof(d__18))] public static IEnumerator LoadAudioCoroutine(string customPath, bool isElsa) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__18(0) { customPath = customPath, isElsa = isElsa }; } [IteratorStateMachine(typeof(d__19))] private static IEnumerator LoadClipFromFile(string filePath, bool isElsa) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__19(0) { filePath = filePath, isElsa = isElsa }; } private static AudioType GetAudioType(string filePath) { return (AudioType)(Path.GetExtension(filePath).ToLowerInvariant() switch { ".ogg" => 14, ".mp3" => 13, ".wav" => 20, _ => 0, }); } } }