Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of SlowSailingBagpipes v1.0.5
plugins/SailingBagpipes.dll
Decompiled 2 weeks agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using Microsoft.CodeAnalysis; using SailingBagpipes.Logging; using UnityEngine; using UnityEngine.Networking; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("SailingBagpipes")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+d5b1b3e35b1c15db102ba9706f1e9199cfd561bb")] [assembly: AssemblyProduct("SailingBagpipes")] [assembly: AssemblyTitle("SailingBagpipes")] [assembly: AssemblyVersion("1.0.0.0")] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } } namespace SailingBagpipes { [BepInPlugin("eh.mataeo.valheim.slowsailingbagpipes", "SlowSailingBagpipes", "1.0.5")] public class Plugin : BaseUnityPlugin { [CompilerGenerated] private sealed class <FadeVolume>d__67 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public Plugin <>4__this; public float fadeDuration; public float targetVolume; public Action onComplete; private float <startVolume>5__2; private float <elapsed>5__3; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <FadeVolume>d__67(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { int num = <>1__state; Plugin plugin = <>4__this; switch (num) { default: return false; case 0: <>1__state = -1; if ((Object)(object)plugin._audioSource == (Object)null) { return false; } if (fadeDuration <= 0f) { plugin._audioSource.volume = targetVolume; plugin._fadeRoutine = null; onComplete?.Invoke(); return false; } <startVolume>5__2 = plugin._audioSource.volume; <elapsed>5__3 = 0f; break; case 1: <>1__state = -1; break; } if (<elapsed>5__3 < fadeDuration) { if ((Object)(object)plugin._audioSource == (Object)null) { return false; } <elapsed>5__3 += Time.deltaTime; float num2 = Mathf.Clamp01(<elapsed>5__3 / fadeDuration); plugin._audioSource.volume = Mathf.Lerp(<startVolume>5__2, targetVolume, num2); <>2__current = null; <>1__state = 1; return true; } if ((Object)(object)plugin._audioSource != (Object)null) { plugin._audioSource.volume = targetVolume; } plugin._fadeRoutine = null; onComplete?.Invoke(); 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 <LoadClipCoroutine>d__71 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public Plugin <>4__this; public string trackPath; private UnityWebRequest <request>5__2; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <LoadClipCoroutine>d__71(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(); } } <request>5__2 = null; <>1__state = -2; } private bool MoveNext() { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Invalid comparison between Unknown and I4 bool result; try { int num = <>1__state; Plugin plugin = <>4__this; switch (num) { default: result = false; break; case 0: { <>1__state = -1; plugin.LogInfo("Loading track " + Path.GetFileName(trackPath) + "."); Uri uri = new Uri(trackPath); AudioType audioTypeForExtension = GetAudioTypeForExtension(Path.GetExtension(trackPath)); <request>5__2 = UnityWebRequestMultimedia.GetAudioClip(uri.AbsoluteUri, audioTypeForExtension); <>1__state = -3; <>2__current = <request>5__2.SendWebRequest(); <>1__state = 1; result = true; break; } case 1: <>1__state = -3; if ((int)<request>5__2.result != 1) { plugin.LogError("Failed to load " + trackPath + ": " + <request>5__2.error); plugin._pendingPlayback = false; plugin._pendingTrackPath = null; plugin._clipLoadRoutine = null; result = false; } else { AudioClip content = DownloadHandlerAudioClip.GetContent(<request>5__2); ((Object)content).name = Path.GetFileNameWithoutExtension(trackPath); plugin._clipCache[trackPath] = content; plugin.LogInfo($"Loaded clip metadata: length={content.length:F1}s channels={content.channels} frequency={content.frequency}."); if (!plugin._pendingPlayback || plugin._pendingTrackPath != trackPath) { plugin._clipLoadRoutine = null; result = false; } else { plugin.PlayClip(content, trackPath); plugin._clipLoadRoutine = null; result = false; } } <>m__Finally1(); break; } } catch { //try-fault ((IDisposable)this).Dispose(); throw; } return result; } 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 (<request>5__2 != null) { ((IDisposable)<request>5__2).Dispose(); } } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private const string PluginGuid = "eh.mataeo.valheim.slowsailingbagpipes"; private const string PluginName = "SlowSailingBagpipes"; private const string PluginVersion = "1.0.5"; private const float PaddleThresholdSeconds = 0.1f; private const float ResumeGraceSeconds = 10f; private const float FadeInDuration = 1.5f; private const float FadeOutDuration = 0.5f; private const string PlaceholderTrackName = "ghost_bagpipe_track.mp3"; private const string DefaultTrackDirectoryName = "BagPipesTracks"; private static readonly string[] SupportedExtensions = new string[3] { ".mp3", ".ogg", ".wav" }; private static readonly FieldInfo? ShipPlayersField = typeof(Ship).GetField("m_players", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly FieldInfo? MusicManMusicSourceField = typeof(MusicMan).GetField("m_musicSource", BindingFlags.Instance | BindingFlags.NonPublic); private ConfigEntry<bool>? _enabled; private ConfigEntry<float>? _volume; private ConfigEntry<string>? _trackDirectoryConfig; private PluginLogger? _fileLogger; private GameObject? _audioHolder; private AudioSource? _audioSource; private readonly Dictionary<string, AudioClip> _clipCache = new Dictionary<string, AudioClip>(); private readonly Random _rng = new Random(); private string? _pluginDirectory; private string? _trackDirectory; private List<string> _trackPaths = new List<string>(); private Coroutine? _clipLoadRoutine; private Coroutine? _fadeRoutine; private string? _pendingTrackPath; private bool _pendingPlayback; private bool _isPlaying; private bool _isPausedForGrace; private float _paddleTimer; private float _resumeUntil; private float _storedMusicVolume = 1f; private bool _musicManSuppressed; private bool _musicManWasPlaying; private bool _audioSourceConfiguredFromMusicMan; private int _lastControlledShipId = -1; private string _lastControlledShipName = "None"; private Speed? _lastSpeedSetting; private bool _lastEligibleRowingState; private string _lastControllerDescription = "None"; private bool? _lastAttachedToShip; private void Awake() { //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Expected O, but got Unknown _pluginDirectory = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location) ?? Paths.PluginPath; _fileLogger = new PluginLogger("SlowSailingBagpipes", _pluginDirectory); _enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Master toggle for the Slow Sailing Bagpipes mod."); _volume = ((BaseUnityPlugin)this).Config.Bind<float>("General", "Volume", 0.85f, new ConfigDescription("Playback volume for the bagpipe loop.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); _trackDirectoryConfig = ((BaseUnityPlugin)this).Config.Bind<string>("Audio", "TrackDirectory", "BagPipesTracks", "Directory (absolute or relative to the plugin folder) that contains bagpipe audio clips."); UpdateTrackDirectory(_trackDirectoryConfig.Value); _audioSource = CreateAudioSource(); ((BaseUnityPlugin)this).Config.SettingChanged += OnConfigSettingChanged; LogInfo("Initialized. Watching track directory: " + _trackDirectory); } private void OnDestroy() { ((BaseUnityPlugin)this).Config.SettingChanged -= OnConfigSettingChanged; StopBagpipes(immediate: true); ClearClipCache(); if ((Object)(object)_audioHolder != (Object)null) { Object.Destroy((Object)(object)_audioHolder); _audioHolder = null; } } private void Update() { //IL_00b4: 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_00e7: Unknown result type (might be due to invalid IL or missing references) ConfigEntry<bool> enabled = _enabled; if (enabled == null || !enabled.Value) { if (_isPlaying || _isPausedForGrace || _pendingPlayback) { StopBagpipes(immediate: true); } return; } if ((Object)(object)_audioSource == (Object)null) { LogWarn("Audio source not initialized; skipping update."); return; } SyncAudioSourceWithMusicMan(); Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { if (_isPlaying || _isPausedForGrace || _pendingPlayback) { StopBagpipes(immediate: true); } LogControlState(null, null, null, "None"); return; } string controllerDescription; Ship val = ResolveControlledShip(localPlayer, out controllerDescription); Speed? speedSetting = ((val != null) ? new Speed?(val.GetSpeedSetting()) : null); LogControlState(localPlayer, val, speedSetting, controllerDescription); if (speedSetting.HasValue && IsRowingAtTriggerSpeed(speedSetting.Value)) { HandleRowingState(val, speedSetting.Value); } else { HandleNonRowingState(val, speedSetting); } } private void OnConfigSettingChanged(object? sender, SettingChangedEventArgs args) { if ((Object)(object)_audioSource == (Object)null) { return; } if (args.ChangedSetting == _volume) { if (_isPlaying) { _audioSource.volume = _volume.Value; } } else if (_trackDirectoryConfig != null && args.ChangedSetting == _trackDirectoryConfig) { LogInfo("Track directory changed to " + _trackDirectoryConfig.Value + "; reloading library."); StopBagpipes(immediate: true); ClearClipCache(); UpdateTrackDirectory(_trackDirectoryConfig.Value); } } private static bool IsRowingAtTriggerSpeed(Speed speedSetting) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Invalid comparison between Unknown and I4 if ((int)speedSetting != 2) { return (int)speedSetting == 1; } return true; } private void HandleRowingState(Ship ship, Speed speedSetting) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) if (!_lastEligibleRowingState) { LogInfo($"Detected eligible rowing on {GetShipDisplayName(ship)} at speed setting {speedSetting}; waiting for {0.1f:F1}s threshold."); } _lastEligibleRowingState = true; _paddleTimer += Time.deltaTime; if (_isPausedForGrace && Time.time <= _resumeUntil) { LogInfo("Resuming paused clip on " + GetShipDisplayName(ship) + " within grace window."); ResumeBagpipes(); return; } bool flag = _resumeUntil > 0f && Time.time <= _resumeUntil; if (!_isPlaying && !_pendingPlayback && !_isPausedForGrace && (_paddleTimer >= 0.1f || flag)) { LogInfo($"Rowing threshold satisfied on {GetShipDisplayName(ship)} at speed setting {speedSetting}; starting bagpipes."); StartBagpipes(); } } private void HandleNonRowingState(Ship? ship, Speed? speedSetting) { //IL_002e: 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) if (_lastEligibleRowingState) { string arg = (((Object)(object)ship != (Object)null) ? GetShipDisplayName(ship) : _lastControlledShipName); object obj; if (!speedSetting.HasValue) { obj = null; } else { Speed valueOrDefault = speedSetting.GetValueOrDefault(); obj = ((object)(Speed)(ref valueOrDefault)).ToString(); } if (obj == null) { obj = "None"; } string arg2 = (string)obj; LogInfo($"Eligible rowing ended on {arg}; current speed setting {arg2}; timer reached {_paddleTimer:F2}s."); } _lastEligibleRowingState = false; _paddleTimer = 0f; if (_pendingPlayback) { CancelPendingPlayback(); } if (_isPlaying) { _resumeUntil = Time.time + 10f; PauseBagpipesForGrace(); } else if (_isPausedForGrace && Time.time > _resumeUntil) { StopBagpipes(immediate: true); } else if (!_isPausedForGrace && Time.time > _resumeUntil) { _resumeUntil = 0f; } } private AudioSource CreateAudioSource() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown _audioHolder = new GameObject("SlowSailingBagpipes_AudioCarrier"); Object.DontDestroyOnLoad((Object)(object)_audioHolder); AudioSource obj = _audioHolder.AddComponent<AudioSource>(); obj.playOnAwake = false; obj.loop = true; obj.volume = 0f; obj.spatialBlend = 0f; obj.priority = 64; return obj; } private void SyncAudioSourceWithMusicMan() { if (!((Object)(object)_audioSource == (Object)null) && !_audioSourceConfiguredFromMusicMan) { AudioSource musicManAudioSource = GetMusicManAudioSource(); if (!((Object)(object)musicManAudioSource == (Object)null)) { _audioSource.outputAudioMixerGroup = musicManAudioSource.outputAudioMixerGroup; _audioSource.priority = musicManAudioSource.priority; _audioSource.pitch = 1f; _audioSource.panStereo = 0f; _audioSource.reverbZoneMix = musicManAudioSource.reverbZoneMix; _audioSource.bypassEffects = musicManAudioSource.bypassEffects; _audioSource.bypassListenerEffects = musicManAudioSource.bypassListenerEffects; _audioSource.bypassReverbZones = musicManAudioSource.bypassReverbZones; _audioSource.ignoreListenerPause = musicManAudioSource.ignoreListenerPause; _audioSource.ignoreListenerVolume = musicManAudioSource.ignoreListenerVolume; _audioSource.mute = false; _audioSource.spatialBlend = 0f; _audioSource.dopplerLevel = 0f; _audioSource.spread = 0f; _audioSourceConfiguredFromMusicMan = true; LogInfo("Audio source synced to Valheim music mixer settings."); } } } private void LogControlState(Player? player, Ship? ship, Speed? speedSetting, string controllerDescription) { //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null) { if (_lastControlledShipId != -1 || _lastSpeedSetting.HasValue || _lastControllerDescription != "None" || _lastAttachedToShip.HasValue) { LogInfo("No local player is active; cleared ship control state."); } _lastControlledShipId = -1; _lastControlledShipName = "None"; _lastSpeedSetting = null; _lastControllerDescription = "None"; _lastAttachedToShip = null; return; } bool flag = ((Character)player).IsAttachedToShip(); if ((Object)(object)ship == (Object)null) { if (_lastControlledShipId != -1 || _lastSpeedSetting.HasValue || controllerDescription != _lastControllerDescription || flag != _lastAttachedToShip) { LogInfo($"No controlled ship resolved. AttachedToShip={flag} controller={controllerDescription}."); } _lastControlledShipId = -1; _lastControlledShipName = "None"; _lastSpeedSetting = null; _lastControllerDescription = controllerDescription; _lastAttachedToShip = flag; return; } int instanceID = ((Object)ship).GetInstanceID(); string shipDisplayName = GetShipDisplayName(ship); if (instanceID != _lastControlledShipId || speedSetting != _lastSpeedSetting || controllerDescription != _lastControllerDescription || flag != _lastAttachedToShip) { LogInfo($"Ship control state: ship={shipDisplayName} speedSetting={speedSetting} speed={ship.GetSpeed():F2} rudder={ship.GetRudder():F2} attached={flag} controller={controllerDescription}."); } _lastControlledShipId = instanceID; _lastControlledShipName = shipDisplayName; _lastSpeedSetting = speedSetting; _lastControllerDescription = controllerDescription; _lastAttachedToShip = flag; } private static string GetShipDisplayName(Ship ship) { string text = (((Object)(object)((Component)ship).gameObject != (Object)null) ? ((Object)((Component)ship).gameObject).name : ((Object)ship).name); if (!string.IsNullOrWhiteSpace(text)) { return text; } return ((object)ship).GetType().Name; } private static Ship? ResolveControlledShip(Player player, out string controllerDescription) { Ship controlledShip = player.GetControlledShip(); if ((Object)(object)controlledShip != (Object)null) { controllerDescription = "Player.GetControlledShip"; return controlledShip; } IDoodadController doodadController = player.GetDoodadController(); if (doodadController == null) { controllerDescription = "None"; return null; } Component controlledComponent = doodadController.GetControlledComponent(); controllerDescription = (((Object)(object)controlledComponent == (Object)null) ? ((object)doodadController).GetType().Name : (((object)doodadController).GetType().Name + "->" + ((object)controlledComponent).GetType().Name)); Ship val = (Ship)(object)((controlledComponent is Ship) ? controlledComponent : null); if (val != null) { return val; } if ((Object)(object)controlledComponent == (Object)null) { return ResolveAttachedShip(player, ref controllerDescription); } object obj = controlledComponent.GetComponent<Ship>() ?? controlledComponent.GetComponentInParent<Ship>(); if (obj == null) { obj = ResolveAttachedShip(player, ref controllerDescription); } return (Ship?)obj; } private static Ship? ResolveAttachedShip(Player player, ref string controllerDescription) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) Ship val = null; float num = float.MaxValue; Ship[] array = Object.FindObjectsByType<Ship>((FindObjectsSortMode)0); foreach (Ship val2 in array) { if (ShipContainsPlayer(val2, player)) { controllerDescription = AppendResolutionSource(controllerDescription, "Ship.m_players"); return val2; } if (((Character)player).IsAttachedToShip()) { float num2 = Vector3.Distance(((Component)player).transform.position, ((Component)val2).transform.position); if (num2 < num) { num = num2; val = val2; } } } if ((Object)(object)val != (Object)null && num <= 25f) { controllerDescription = AppendResolutionSource(controllerDescription, $"NearestAttachedShip({num:F1}m)"); return val; } return null; } private static bool ShipContainsPlayer(Ship ship, Player player) { if (!(ShipPlayersField?.GetValue(ship) is List<Player> list)) { return false; } return list.Contains(player); } private static string AppendResolutionSource(string currentDescription, string source) { if (string.IsNullOrWhiteSpace(currentDescription) || currentDescription == "None") { return source; } return currentDescription + "+" + source; } private void EnsurePlaceholderTrackExists(string trackDirectory) { string path = Path.Combine(trackDirectory, "ghost_bagpipe_track.mp3"); if (!File.Exists(path)) { File.WriteAllBytes(path, Array.Empty<byte>()); LogInfo("Created placeholder track file. Replace it with a real MP3, OGG, or WAV file."); } } private void RefreshTrackLibrary() { if (_trackDirectory != null) { _trackPaths = (from path in Directory.EnumerateFiles(_trackDirectory, "*.*", SearchOption.TopDirectoryOnly) where SupportedExtensions.Contains<string>(Path.GetExtension(path), StringComparer.OrdinalIgnoreCase) where new FileInfo(path).Length > 0 select path).OrderBy<string, string>((string path) => path, StringComparer.OrdinalIgnoreCase).ToList(); if (_trackPaths.Count == 0) { LogWarn("No playable bagpipe tracks found in " + _trackDirectory + ". Add non-empty MP3, OGG, or WAV files."); } else { LogInfo($"Loaded {_trackPaths.Count} playable bagpipe track(s) from {_trackDirectory}."); } } } private void UpdateTrackDirectory(string directorySetting) { if (string.IsNullOrWhiteSpace(directorySetting)) { directorySetting = "BagPipesTracks"; if (_trackDirectoryConfig != null) { _trackDirectoryConfig.Value = "BagPipesTracks"; } } string text = (_trackDirectory = ResolveTrackDirectory(directorySetting)); Directory.CreateDirectory(text); if (IsDefaultTrackDirectory(directorySetting)) { EnsurePlaceholderTrackExists(text); } RefreshTrackLibrary(); } private string ResolveTrackDirectory(string configuredPath) { if (Path.IsPathRooted(configuredPath)) { return configuredPath; } return Path.Combine(_pluginDirectory ?? Paths.PluginPath, configuredPath); } private static bool IsDefaultTrackDirectory(string directorySetting) { return string.Equals(directorySetting, "BagPipesTracks", StringComparison.OrdinalIgnoreCase); } private void StartBagpipes() { if ((Object)(object)_audioSource == (Object)null) { LogWarn("Audio source missing; cannot start playback."); return; } SyncAudioSourceWithMusicMan(); if (_trackPaths.Count == 0) { RefreshTrackLibrary(); if (_trackPaths.Count == 0) { return; } } string text = SelectRandomTrack(); if (text == null) { LogWarn("Track selection returned no result."); return; } _pendingPlayback = true; _pendingTrackPath = text; if (_clipCache.TryGetValue(text, out AudioClip value) && (Object)(object)value != (Object)null) { PlayClip(value, text); return; } CancelFade(); if (_clipLoadRoutine != null) { ((MonoBehaviour)this).StopCoroutine(_clipLoadRoutine); } _clipLoadRoutine = ((MonoBehaviour)this).StartCoroutine(LoadClipCoroutine(text)); } private void PauseBagpipesForGrace() { if ((Object)(object)_audioSource == (Object)null || (Object)(object)_audioSource.clip == (Object)null || _isPausedForGrace) { return; } _isPlaying = false; _isPausedForGrace = true; StartFade(0f, 0.5f, delegate { if (!((Object)(object)_audioSource == (Object)null)) { if (_isPausedForGrace && Time.time <= _resumeUntil) { _audioSource.Pause(); } else { _audioSource.Stop(); _audioSource.clip = null; _isPausedForGrace = false; } SetGameMusicSuppressed(shouldSuppress: false); } }); } private void ResumeBagpipes() { if ((Object)(object)_audioSource == (Object)null || (Object)(object)_audioSource.clip == (Object)null) { _isPausedForGrace = false; StartBagpipes(); return; } CancelFade(); if (!_audioSource.isPlaying) { _audioSource.UnPause(); } _isPausedForGrace = false; _isPlaying = true; SetGameMusicSuppressed(shouldSuppress: true); StartFade(_volume.Value, 1.5f); } private void StopBagpipes(bool immediate = false) { CancelPendingPlayback(); if ((Object)(object)_audioSource == (Object)null) { _isPlaying = false; _isPausedForGrace = false; _resumeUntil = 0f; return; } CancelFade(); _isPlaying = false; _isPausedForGrace = false; _resumeUntil = 0f; if (immediate) { _audioSource.Stop(); _audioSource.clip = null; _audioSource.volume = 0f; SetGameMusicSuppressed(shouldSuppress: false); return; } StartFade(0f, 0.5f, delegate { if (!((Object)(object)_audioSource == (Object)null)) { _audioSource.Stop(); _audioSource.clip = null; SetGameMusicSuppressed(shouldSuppress: false); } }); } private void StartFade(float targetVolume, float fadeDuration, Action? onComplete = null) { CancelFade(); _fadeRoutine = ((MonoBehaviour)this).StartCoroutine(FadeVolume(targetVolume, fadeDuration, onComplete)); } private void CancelFade() { if (_fadeRoutine != null) { ((MonoBehaviour)this).StopCoroutine(_fadeRoutine); _fadeRoutine = null; } } [IteratorStateMachine(typeof(<FadeVolume>d__67))] private IEnumerator FadeVolume(float targetVolume, float fadeDuration, Action? onComplete = null) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <FadeVolume>d__67(0) { <>4__this = this, targetVolume = targetVolume, fadeDuration = fadeDuration, onComplete = onComplete }; } private void SetGameMusicSuppressed(bool shouldSuppress) { AudioSource musicManAudioSource = GetMusicManAudioSource(); if ((Object)(object)musicManAudioSource == (Object)null) { return; } if (shouldSuppress && !_musicManSuppressed) { _storedMusicVolume = musicManAudioSource.volume; _musicManWasPlaying = musicManAudioSource.isPlaying; musicManAudioSource.volume = 0f; if (_musicManWasPlaying) { musicManAudioSource.Pause(); } _musicManSuppressed = true; } else if (!shouldSuppress && _musicManSuppressed) { musicManAudioSource.volume = _storedMusicVolume; if (_musicManWasPlaying) { musicManAudioSource.UnPause(); } _musicManSuppressed = false; _musicManWasPlaying = false; } } private AudioSource? GetMusicManAudioSource() { MusicMan instance = MusicMan.instance; if ((Object)(object)instance == (Object)null || MusicManMusicSourceField == null) { return null; } try { object? value = MusicManMusicSourceField.GetValue(instance); return (AudioSource?)((value is AudioSource) ? value : null); } catch (Exception ex) { LogWarn("Unable to resolve MusicMan audio source via reflection: " + ex.GetType().Name + ": " + ex.Message); return null; } } private string? SelectRandomTrack() { if (_trackPaths.Count == 0) { return null; } int index = _rng.Next(0, _trackPaths.Count); return _trackPaths[index]; } [IteratorStateMachine(typeof(<LoadClipCoroutine>d__71))] private IEnumerator LoadClipCoroutine(string trackPath) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <LoadClipCoroutine>d__71(0) { <>4__this = this, trackPath = trackPath }; } private void PlayClip(AudioClip clip, string trackPath) { if ((Object)(object)_audioSource == (Object)null) { LogWarn("Audio source missing during playback start."); return; } LogInfo("Starting clip " + Path.GetFileName(trackPath) + "."); CancelFade(); _pendingPlayback = false; _pendingTrackPath = null; _isPausedForGrace = false; _isPlaying = true; _audioSource.clip = clip; _audioSource.volume = 0f; _audioSource.mute = false; _audioSource.Play(); SetGameMusicSuppressed(shouldSuppress: true); StartFade(_volume.Value, 1.5f); } private void CancelPendingPlayback() { _pendingPlayback = false; _pendingTrackPath = null; if (_clipLoadRoutine != null) { ((MonoBehaviour)this).StopCoroutine(_clipLoadRoutine); _clipLoadRoutine = null; } } private void ClearClipCache() { foreach (AudioClip value in _clipCache.Values) { if ((Object)(object)value != (Object)null) { Object.Destroy((Object)(object)value); } } _clipCache.Clear(); } private static AudioType GetAudioTypeForExtension(string extension) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) return (AudioType)(extension.ToLowerInvariant() switch { ".mp3" => 13, ".ogg" => 14, ".wav" => 20, _ => 0, }); } private void LogDebug(string message) { ((BaseUnityPlugin)this).Logger.LogDebug((object)message); _fileLogger?.Debug(message); } private void LogInfo(string message) { ((BaseUnityPlugin)this).Logger.LogInfo((object)message); _fileLogger?.Info(message); } private void LogWarn(string message) { ((BaseUnityPlugin)this).Logger.LogWarning((object)message); _fileLogger?.Warn(message); } private void LogError(string message) { ((BaseUnityPlugin)this).Logger.LogError((object)message); _fileLogger?.Error(message); } } } namespace SailingBagpipes.Logging { internal sealed class PluginLogger { private const int MaxLogFiles = 7; private readonly string _projectName; private readonly string _logDirectory; private readonly string _logPath; private readonly object _gate = new object(); internal PluginLogger(string projectName, string pluginDirectory) { _projectName = projectName; _logDirectory = Path.Combine(pluginDirectory, "Logs"); Directory.CreateDirectory(_logDirectory); _logPath = Path.Combine(_logDirectory, $"{_projectName}-{DateTime.Now:yyyy-MM-dd-HH-mm}.log"); CleanupOldLogs(); } internal void Debug(string message) { Write("DEBUG", message); } internal void Info(string message) { Write("INFO", message); } internal void Warn(string message) { Write("WARN", message); } internal void Error(string message) { Write("ERROR", message); } private void Write(string level, string message) { string text = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}][{level}] {message}"; lock (_gate) { File.AppendAllText(_logPath, text + Environment.NewLine); } } private void CleanupOldLogs() { foreach (string item in Directory.GetFiles(_logDirectory, _projectName + "-*.log").OrderByDescending(File.GetCreationTimeUtc).ToList() .Skip(7)) { try { File.Delete(item); } catch (IOException) { } } } } }