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 Shanty4Heim v1.3.2
plugins/SeaShanty4Heim.dll
Decompiled a week agousing System; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; using Valheim.SettingsGui; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("SeaShanty4Heim")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("SeaShanty4Heim")] [assembly: AssemblyCopyright("Copyright © 2025")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("82659a80-5ea4-4861-89b2-1482c7681b90")] [assembly: AssemblyFileVersion("1.2.1.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("1.2.1.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 SeaShanty4Heim { [BepInPlugin("com.jonny.seashanty4heim", "SeaShanty4Heim", "1.3.2")] public class SeaShantyPlugin : BaseUnityPlugin { private sealed class NamedTrack { public readonly string PlayerName; public readonly string Resource; public readonly Regex Pattern; public AudioClip Clip; public NamedTrack(string playerName, string resource) { PlayerName = playerName; Resource = resource; Pattern = new Regex("(?<![\\p{L}\\p{N}])" + Regex.Escape(playerName) + "(?![\\p{L}\\p{N}])", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); } } [HarmonyPatch(typeof(Player), "OnSkillLevelup")] private static class Patch_SkillLevelup { private static void Postfix(Player __instance) { if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && !((Object)(object)levelUpSource == (Object)null) && ShantyVolume.LevelUpLive) { levelUpSource.volume = ShantyVolume.Effective; levelUpSource.PlayOneShot(levelUp); } } } public const string PluginGuid = "com.jonny.seashanty4heim"; public const string PluginName = "SeaShanty4Heim"; public const string PluginVersion = "1.3.2"; private const string ShantyResource = "SeaShanty4Heim.Assets.Sea_Shanty_2.wav"; private const string LevelUpResource = "SeaShanty4Heim.Assets.Engineer.wav"; private const string RoarResource = "SeaShanty4Heim.Assets.ShowerHandle.wav"; private const string SourceName = "SeaShanty4Heim_Source"; private const string DanceEmote = "dance"; private const string HeadbangEmote = "headbang"; private const string RoarEmote = "roar"; private readonly NamedTrack[] shipTracks = new NamedTrack[1] { new NamedTrack("Bjarne", "SeaShanty4Heim.Assets.Bjarne.wav") }; private readonly NamedTrack[] landTracks = new NamedTrack[2] { new NamedTrack("Boris", "SeaShanty4Heim.Assets.Boris.wav"), new NamedTrack("Tanka Jahari", "SeaShanty4Heim.Assets.chicago.wav") }; private AudioClip shanty; private AudioClip roar; private bool haveAudio; private static AudioClip levelUp; private static AudioSource levelUpSource; private void Awake() { //IL_015e: Unknown result type (might be due to invalid IL or missing references) ShantyVolume.Log = ((BaseUnityPlugin)this).Logger; ShantyVolume.Config = ((BaseUnityPlugin)this).Config.Bind<float>("Audio", "Volume", 0.6f, "Playback volume, 0 to 1. Also adjustable in game under Settings > Audio."); ShantyVolume.Live = ShantyVolume.Config.Value; ShantyVolume.LevelUpConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("Audio", "LevelUpSound", true, "Play a jingle when one of your own skills levels up. Also toggleable in game under Settings > Audio."); ShantyVolume.LevelUpLive = ShantyVolume.LevelUpConfig.Value; ShantyVolume.Master = PlatformPrefs.GetFloat("MasterVolume", 1f); shanty = TryLoad("SeaShanty4Heim.Assets.Sea_Shanty_2.wav", "SeaShanty2", required: true); haveAudio = (Object)(object)shanty != (Object)null; haveAudio |= LoadAll(shipTracks); haveAudio |= LoadAll(landTracks); roar = TryLoad("SeaShanty4Heim.Assets.ShowerHandle.wav", "ShowerHandle", required: false); haveAudio |= (Object)(object)roar != (Object)null; levelUp = TryLoad("SeaShanty4Heim.Assets.Engineer.wav", "Engineer", required: false); if ((Object)(object)levelUp != (Object)null) { levelUpSource = ((Component)this).gameObject.AddComponent<AudioSource>(); levelUpSource.playOnAwake = false; levelUpSource.spatialBlend = 0f; } new Harmony("com.jonny.seashanty4heim").PatchAll(); } private void Update() { if (!haveAudio) { return; } foreach (Player allPlayer in Player.GetAllPlayers()) { if ((Object)(object)allPlayer == (Object)null) { continue; } AudioClip val = TrackFor(allPlayer); AudioSource val2 = FindSource(allPlayer); if ((Object)(object)val == (Object)null) { if ((Object)(object)val2 != (Object)null && (!((Object)(object)val2.clip == (Object)(object)roar) || !val2.isPlaying)) { Object.Destroy((Object)(object)((Component)val2).gameObject); } continue; } if ((Object)(object)val2 == (Object)null) { val2 = CreateSource(allPlayer); } val2.volume = ShantyVolume.Effective; if ((Object)(object)val2.clip != (Object)(object)val || !val2.isPlaying) { val2.clip = val; val2.loop = (Object)(object)val != (Object)(object)roar; val2.timeSamples = ((!((Object)(object)val == (Object)(object)roar)) ? PlayheadFor(val) : 0); val2.Play(); } } } private AudioClip TrackFor(Player player) { string text = CurrentEmote(player); if (text == null) { return null; } string playerName = player.GetPlayerName(); bool flag = ((Character)player).InNumShipVolumes > 0; if (text == "roar") { if (!IsShowerHandle(playerName)) { return null; } return roar; } if (text == "headbang") { if (!flag) { return null; } return Match(shipTracks, playerName); } if (text != "dance") { return null; } if (!flag) { return Match(landTracks, playerName); } return shanty; } private static string CurrentEmote(Player player) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) if (!((Character)player).InEmote()) { return null; } ZDOMan instance = ZDOMan.instance; ZDO obj = ((instance != null) ? instance.GetZDO(((Character)player).GetZDOID()) : null); if (obj == null) { return null; } return obj.GetString(ZDOVars.s_emote, string.Empty); } private static bool IsShowerHandle(string playerName) { if (playerName.IndexOf("shower", StringComparison.OrdinalIgnoreCase) >= 0) { return playerName.IndexOf("handle", StringComparison.OrdinalIgnoreCase) >= 0; } return false; } private static AudioClip Match(NamedTrack[] tracks, string playerName) { if (string.IsNullOrEmpty(playerName)) { return null; } foreach (NamedTrack namedTrack in tracks) { if ((Object)(object)namedTrack.Clip != (Object)null && namedTrack.Pattern.IsMatch(playerName)) { return namedTrack.Clip; } } return null; } private bool LoadAll(NamedTrack[] tracks) { bool flag = false; foreach (NamedTrack namedTrack in tracks) { namedTrack.Clip = TryLoad(namedTrack.Resource, namedTrack.PlayerName, required: false); flag |= (Object)(object)namedTrack.Clip != (Object)null; } return flag; } private AudioSource CreateSource(Player player) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("SeaShanty4Heim_Source"); val.transform.SetParent(((Component)player).transform, false); AudioSource obj = val.AddComponent<AudioSource>(); obj.loop = true; obj.spatialBlend = 1f; obj.rolloffMode = (AudioRolloffMode)1; obj.minDistance = 5f; obj.maxDistance = 50f; obj.dopplerLevel = 0f; return obj; } private int PlayheadFor(AudioClip clip) { foreach (Player allPlayer in Player.GetAllPlayers()) { AudioSource val = FindSource(allPlayer); if ((Object)(object)val != (Object)null && val.isPlaying && (Object)(object)val.clip == (Object)(object)clip) { return val.timeSamples; } } return 0; } private static AudioSource FindSource(Player player) { if ((Object)(object)player == (Object)null) { return null; } Transform val = ((Component)player).transform.Find("SeaShanty4Heim_Source"); if (!((Object)(object)val == (Object)null)) { return ((Component)val).GetComponent<AudioSource>(); } return null; } private AudioClip TryLoad(string resourceName, string clipName, bool required) { try { AudioClip val = LoadEmbeddedClip(resourceName, clipName); ((BaseUnityPlugin)this).Logger.LogInfo((object)$"Loaded {clipName}: {val.length:F0}s, {val.channels}ch @ {val.frequency}Hz."); return val; } catch (FileNotFoundException) when (!required) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("No " + clipName + " track embedded in this build, skipping it.")); return null; } catch (Exception ex2) { ((BaseUnityPlugin)this).Logger.LogError((object)("Could not load " + clipName + ", it will stay silent: " + ex2.Message)); return null; } } private AudioClip LoadEmbeddedClip(string resourceName, string clipName) { Assembly executingAssembly = Assembly.GetExecutingAssembly(); using Stream stream = executingAssembly.GetManifestResourceStream(resourceName); if (stream == null) { throw new FileNotFoundException("embedded resource '" + resourceName + "' is missing, the assembly has: " + string.Join(", ", executingAssembly.GetManifestResourceNames())); } byte[] array = new byte[stream.Length]; int num; for (int i = 0; i < array.Length; i += num) { num = stream.Read(array, i, array.Length - i); if (num == 0) { throw new EndOfStreamException($"resource ended after {i} of {array.Length} bytes"); } } int channels; int frequency; float[] array2 = WavReader.Read(array, out channels, out frequency); AudioClip obj = AudioClip.Create(clipName, array2.Length / channels, channels, frequency, false); obj.SetData(array2, 0); return obj; } } public static class WavReader { public static float[] Read(byte[] wav, out int channels, out int frequency) { if (wav == null || wav.Length < 44 || Tag(wav, 0) != "RIFF" || Tag(wav, 8) != "WAVE") { throw new FormatException("not a RIFF/WAVE file"); } channels = 0; frequency = 0; int num = 0; int num2 = -1; int num3 = 0; int num4 = 12; while (num4 + 8 <= wav.Length) { string text = Tag(wav, num4); int num5 = BitConverter.ToInt32(wav, num4 + 4); int num6 = num4 + 8; if (num5 < 0 || num6 + num5 > wav.Length) { num5 = wav.Length - num6; } if (text == "fmt " && num5 >= 16) { channels = BitConverter.ToInt16(wav, num6 + 2); frequency = BitConverter.ToInt32(wav, num6 + 4); num = BitConverter.ToInt16(wav, num6 + 14); } else if (text == "data") { num2 = num6; num3 = num5; break; } num4 = num6 + num5 + (num5 & 1); } if (channels <= 0 || frequency <= 0) { throw new FormatException("no usable fmt chunk"); } if (num2 < 0) { throw new FormatException("no data chunk"); } if (num != 16) { throw new FormatException($"{num}-bit audio; re-save the file as 16-bit PCM"); } float[] array = new float[num3 / 2]; for (int i = 0; i < array.Length; i++) { int num7 = num2 + i * 2; array[i] = (float)(short)(wav[num7] | (wav[num7 + 1] << 8)) / 32768f; } return array; } private static string Tag(byte[] wav, int at) { return Encoding.ASCII.GetString(wav, at, 4); } } public static class ShantyVolume { [HarmonyPatch(typeof(AudioSettings), "Initialize")] private static class Patch_Initialize { private static void Postfix(AudioSettings __instance) { try { Build(__instance); } catch (Exception arg) { Log.LogError((object)string.Format("Could not add the '{0}' slider: {1}", "Shanty4Heim Sound", arg)); } } } [HarmonyPatch(typeof(AudioSettings), "OnOkAsync")] private static class Patch_OnOk { private static void Prefix() { if ((Object)(object)slider != (Object)null) { Config.Value = Mathf.Clamp01(slider.value); } if ((Object)(object)levelUpToggle != (Object)null) { LevelUpConfig.Value = levelUpToggle.isOn; } Live = Mathf.Clamp01(Config.Value); LevelUpLive = LevelUpConfig.Value; } } [HarmonyPatch(typeof(AudioSettings), "OnBack")] private static class Patch_OnBack { private static void Postfix() { Live = Mathf.Clamp01(Config.Value); LevelUpLive = LevelUpConfig.Value; } } [HarmonyPatch(typeof(AudioSettings), "SetGlobalVolumeExceptHaptics")] private static class Patch_Master { private static void Prefix(float __0) { Master = Mathf.Clamp01(__0); } } public const string Label = "Shanty4Heim Sound"; public const string LevelUpLabel = "Shanty4Heim Level-up Sound"; private const string RowName = "Shanty4HeimVolumeRow"; private const string LevelUpRowName = "Shanty4HeimLevelUpRow"; public static ManualLogSource Log; public static ConfigEntry<float> Config; public static ConfigEntry<bool> LevelUpConfig; public static float Live = 0.6f; public static bool LevelUpLive = true; public static float Master = 1f; private static Slider slider; private static Toggle levelUpToggle; public static float Effective => Mathf.Clamp01(Live) * Mathf.Clamp01(Master); private static void UpdateText(TMP_Text text, float value) { if ((Object)(object)text != (Object)null) { text.text = Mathf.Round(value * 100f) + "%"; } } private static Transform RowOf(Transform a0, Transform b0) { Transform val = a0; while ((Object)(object)val != (Object)null) { Transform val2 = b0; while ((Object)(object)val2 != (Object)null) { if ((Object)(object)val != (Object)(object)val2 && (Object)(object)val.parent != (Object)null && (Object)(object)val.parent == (Object)(object)val2.parent) { return val; } val2 = val2.parent; } val = val.parent; } if (!((Object)(object)a0.parent != (Object)null)) { return a0; } return a0.parent; } private static void Build(AudioSettings settings) { //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Expected O, but got Unknown Traverse obj = Traverse.Create((object)settings); Slider value = obj.Field("m_musicVolumeSlider").GetValue<Slider>(); Slider value2 = obj.Field("m_sfxVolumeSlider").GetValue<Slider>(); TMP_Text value3 = obj.Field("m_musicVolumeText").GetValue<TMP_Text>(); Toggle value4 = obj.Field("m_continousMusic").GetValue<Toggle>(); if ((Object)(object)value == (Object)null || (Object)(object)value2 == (Object)null) { Log.LogWarning((object)"Audio settings sliders not found; leaving the settings tab alone."); return; } Transform val = RowOf(((Component)value).transform, ((Component)value2).transform); if ((Object)(object)val.parent == (Object)null) { Log.LogWarning((object)("Music row '" + ((Object)val).name + "' has no parent to add a row to.")); } else { if ((Object)(object)val.parent.Find("Shanty4HeimVolumeRow") != (Object)null) { return; } Transform val2 = Object.Instantiate<Transform>(val, val.parent); ((Object)val2).name = "Shanty4HeimVolumeRow"; val2.SetSiblingIndex(val.GetSiblingIndex() + 1); slider = ((Component)val2).GetComponentInChildren<Slider>(true); if ((Object)(object)slider == (Object)null) { Log.LogWarning((object)("Cloned row '" + ((Object)val).name + "' had no Slider; removing it again.")); Object.Destroy((Object)(object)((Component)val2).gameObject); return; } slider.onValueChanged = new SliderEvent(); slider.SetValueWithoutNotify(Mathf.Clamp(Config.Value, slider.minValue, slider.maxValue)); TMP_Text[] componentsInChildren = ((Component)val).GetComponentsInChildren<TMP_Text>(true); TMP_Text[] componentsInChildren2 = ((Component)val2).GetComponentsInChildren<TMP_Text>(true); int num = Array.IndexOf(componentsInChildren, value3); TMP_Text valueText = null; for (int i = 0; i < componentsInChildren2.Length; i++) { if (i == num) { valueText = componentsInChildren2[i]; } else { componentsInChildren2[i].text = "Shanty4Heim Sound"; } } UpdateText(valueText, slider.value); ((UnityEvent<float>)(object)slider.onValueChanged).AddListener((UnityAction<float>)delegate(float num2) { Live = num2; UpdateText(valueText, num2); }); Log.LogInfo((object)("Added the 'Shanty4Heim Sound' slider under '" + ((Object)val).name + "' " + $"({componentsInChildren2.Length} label(s), readout index {num}).")); BuildToggle(value4, ((Component)value).transform, val2); } } private static void BuildToggle(Toggle musicToggle, Transform musicSlider, Transform shantyRow) { //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Expected O, but got Unknown if ((Object)(object)musicToggle == (Object)null) { Log.LogWarning((object)"Continuous music toggle not found; leaving the checkbox out."); return; } Transform val = RowOf(((Component)musicToggle).transform, musicSlider); if (((Component)val).GetComponentsInChildren<Toggle>(true).Length != 1 || (Object)(object)((Component)val).GetComponentInChildren<Slider>(true) != (Object)null) { Log.LogWarning((object)("'" + ((Object)val).name + "' does not look like a single checkbox row; leaving the checkbox out.")); return; } Transform val2 = Object.Instantiate<Transform>(val, shantyRow.parent); ((Object)val2).name = "Shanty4HeimLevelUpRow"; val2.SetSiblingIndex(shantyRow.GetSiblingIndex() + 1); levelUpToggle = ((Component)val2).GetComponentInChildren<Toggle>(true); if ((Object)(object)levelUpToggle == (Object)null) { Log.LogWarning((object)("Cloned row '" + ((Object)val).name + "' had no Toggle; removing it again.")); Object.Destroy((Object)(object)((Component)val2).gameObject); return; } levelUpToggle.onValueChanged = new ToggleEvent(); levelUpToggle.SetIsOnWithoutNotify(LevelUpConfig.Value); ((UnityEvent<bool>)(object)levelUpToggle.onValueChanged).AddListener((UnityAction<bool>)delegate(bool value) { LevelUpLive = value; }); TMP_Text[] componentsInChildren = ((Component)val2).GetComponentsInChildren<TMP_Text>(true); for (int num = 0; num < componentsInChildren.Length; num++) { componentsInChildren[num].text = "Shanty4Heim Level-up Sound"; } Log.LogInfo((object)"Added the 'Shanty4Heim Level-up Sound' checkbox under the 'Shanty4Heim Sound' slider."); } } }