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 FishCues v0.1.1
FishCues.dll
Decompiled 2 weeks agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("assembly_valheim")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("FishCues")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.1.1.0")] [assembly: AssemblyInformationalVersion("0.1.1+71e76beb6276bed7b59df336ec1182615c484abf")] [assembly: AssemblyProduct("FishCues")] [assembly: AssemblyTitle("FishCues")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.1.1.0")] [module: UnverifiableCode] [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 FishCues { internal static class Clips { private const float RescanSeconds = 15f; private static Dictionary<string, AudioClip> _byName; private static float _nextScan = -1f; private static readonly HashSet<string> Warned = new HashSet<string>(StringComparer.OrdinalIgnoreCase); public static AudioClip Find(string preferred, params string[] fallbacks) { Dictionary<string, AudioClip> dictionary = Map(); if (dictionary == null) { return null; } if (!string.IsNullOrWhiteSpace(preferred) && dictionary.TryGetValue(preferred.Trim(), out var value)) { return value; } if (fallbacks != null) { foreach (string key in fallbacks) { if (dictionary.TryGetValue(key, out value)) { return value; } } } return null; } public static void WarnMissing(string role, string configKey, string preferred) { if (Warned.Add(role) && FishCuesPlugin.Log != null) { FishCuesPlugin.Log.LogWarning((object)("FishCues: no game sound called '" + preferred + "', so the " + role + " cue is silent. Set " + configKey + " to any sound name from the game.")); } } private static Dictionary<string, AudioClip> Map() { if (_byName != null && _byName.Count > 0) { return _byName; } if (Time.time < _nextScan) { return null; } _nextScan = Time.time + 15f; AudioClip[] array = Resources.FindObjectsOfTypeAll<AudioClip>(); if (array == null || array.Length == 0) { return null; } Dictionary<string, AudioClip> dictionary = new Dictionary<string, AudioClip>(array.Length, StringComparer.OrdinalIgnoreCase); foreach (AudioClip val in array) { if ((Object)(object)val != (Object)null && !string.IsNullOrEmpty(((Object)val).name) && !dictionary.ContainsKey(((Object)val).name)) { dictionary[((Object)val).name] = val; } } _byName = dictionary; ManualLogSource log = FishCuesPlugin.Log; if (log != null) { log.LogDebug((object)$"FishCues: indexed {dictionary.Count} game sounds."); } return dictionary; } } [HarmonyPatch(typeof(FishingFloat), "RPC_Nibble")] internal static class NibblePatch { private static void Prefix(FishingFloat __instance, out float __state) { __state = __instance.m_nibbleTime; } private static void Postfix(FishingFloat __instance, bool correctBait, float __state) { try { if (correctBait && __instance.m_nibbleTime != __state) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null && FishCuesPlugin.IsMine(__instance, localPlayer)) { FishCuesPlugin.Plink(); } } } catch (Exception e) { FishCuesPlugin.WarnOnce("NibblePatch", e); } } } [BepInPlugin("online.buddycloud.fishcues", "FishCues", "0.1.1")] public class FishCuesPlugin : BaseUnityPlugin { public const string PluginGuid = "online.buddycloud.fishcues"; public const string PluginName = "FishCues"; public const string PluginVersion = "0.1.1"; internal static ManualLogSource Log; internal static FishCuesPlugin Self; internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<string> PlinkClip; internal static ConfigEntry<float> PlinkVolume; internal static ConfigEntry<string> StruggleClip; internal static ConfigEntry<float> StruggleVolume; internal static ConfigEntry<float> StrugglePitch; private AudioSource _plinkSource; private AudioSource _toneSource; private string _plinkName; private string _struggleName; private static readonly string[] PlinkFallbacks = new string[3] { "Splash_Water_Small3", "Splash_Water_Small1", "UI_Craft_Finish_01" }; private static readonly string[] StruggleFallbacks = new string[3] { "Tar_Bubble_Loop", "Boil_CauldronBubble_Loop", "Amb_Caves_WaterDrop_01" }; private static readonly HashSet<string> Warned = new HashSet<string>(); private void Awake() { Log = ((BaseUnityPlugin)this).Logger; Self = this; Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Play the fishing audio cues."); PlinkClip = ((BaseUnityPlugin)this).Config.Bind<string>("General", "PlinkClip", "Splash_Water_Small2", "Name of any vanilla game sound to play when a fish bites. Defaults to a small water splash - wet rather than alarming, and it belongs to fishing rather than to the crafting menu. Free text: the game holds thousands of sounds."); PlinkVolume = ((BaseUnityPlugin)this).Config.Bind<float>("General", "PlinkVolume", 0.15f, "Volume of the 'plink' played when a fish bites (0 disables just the plink). The splash peaks near 0 dBFS so it reads louder than the number: 0.3 is +6 dB, 0.1 -3.5 dB."); StruggleClip = ((BaseUnityPlugin)this).Config.Bind<string>("General", "StruggleClip", "Items_Bathtub_Bubbles_Loop", "Looped sound played while the hooked fish struggles. Defaults to a bathtub's bubbles: 21 s long, so no audible cycle, and the bathtub is the only thing in the game that plays it - you will never hear it by accident while fishing."); StruggleVolume = ((BaseUnityPlugin)this).Config.Bind<float>("General", "StruggleVolume", 0.25f, "Volume of the tone played while the hooked fish struggles (0 disables just the tone). Silence means the fish is calm."); StrugglePitch = ((BaseUnityPlugin)this).Config.Bind<float>("General", "StrugglePitch", 1f, "Pitch of the struggle tone, 0.5 to 2. Raise it if the tone hides under water and wind."); Harmony.CreateAndPatchAll(typeof(FishCuesPlugin).Assembly, "online.buddycloud.fishcues"); Log.LogInfo((object)"FishCues 0.1.1: plink on the bite, tone while the fish struggles."); } private void Update() { try { EnsureSources(); if (!Enabled.Value || StruggleVolume.Value <= 0f) { StopTone(); return; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { StopTone(); return; } Fish val = MyCatch(localPlayer); if ((Object)(object)val != (Object)null && val.IsEscaping()) { StartTone(); } else { StopTone(); } } catch (Exception e) { WarnOnce("Update", e); } } private void OnDestroy() { StopTone(); } private void EnsureSources() { if ((Object)(object)_plinkSource == (Object)null) { _plinkSource = ((Component)this).gameObject.AddComponent<AudioSource>(); _plinkSource.playOnAwake = false; _plinkSource.loop = false; _plinkSource.spatialBlend = 0f; } if ((Object)(object)_toneSource == (Object)null) { _toneSource = ((Component)this).gameObject.AddComponent<AudioSource>(); _toneSource.playOnAwake = false; _toneSource.loop = true; _toneSource.spatialBlend = 0f; } RefreshClips(); if ((Object)(object)AudioMan.instance != (Object)null) { if ((Object)(object)_plinkSource.outputAudioMixerGroup == (Object)null) { _plinkSource.outputAudioMixerGroup = AudioMan.instance.m_guiMixer; } if ((Object)(object)_toneSource.outputAudioMixerGroup == (Object)null) { _toneSource.outputAudioMixerGroup = AudioMan.instance.m_guiMixer; } } } private void RefreshClips() { if ((Object)(object)_plinkSource.clip == (Object)null || !string.Equals(_plinkName, PlinkClip.Value, StringComparison.OrdinalIgnoreCase)) { _plinkName = PlinkClip.Value; _plinkSource.clip = Clips.Find(_plinkName, PlinkFallbacks); if ((Object)(object)_plinkSource.clip == (Object)null) { Clips.WarnMissing("bite", "PlinkClip", _plinkName); } } if ((Object)(object)_toneSource.clip == (Object)null || !string.Equals(_struggleName, StruggleClip.Value, StringComparison.OrdinalIgnoreCase)) { _struggleName = StruggleClip.Value; _toneSource.clip = Clips.Find(_struggleName, StruggleFallbacks); if ((Object)(object)_toneSource.clip == (Object)null) { Clips.WarnMissing("struggle", "StruggleClip", _struggleName); } } } private void StartTone() { if (!((Object)(object)_toneSource.clip == (Object)null)) { _toneSource.volume = Mathf.Clamp01(StruggleVolume.Value); _toneSource.pitch = Mathf.Clamp(StrugglePitch.Value, 0.5f, 2f); if (!_toneSource.isPlaying) { _toneSource.Play(); Log.LogDebug((object)"Struggle tone on."); } } } private void StopTone() { if ((Object)(object)_toneSource != (Object)null && _toneSource.isPlaying) { _toneSource.Stop(); Log.LogDebug((object)"Struggle tone off."); } } private static Fish MyCatch(Player player) { foreach (FishingFloat allInstance in FishingFloat.GetAllInstances()) { if (IsMine(allInstance, player)) { Fish val = allInstance.GetCatch(); if ((Object)(object)val != (Object)null) { return val; } } } return null; } internal static bool IsMine(FishingFloat ff, Player player) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ff == (Object)null || (Object)(object)ff.m_nview == (Object)null || !ff.m_nview.IsValid()) { return false; } long num = ff.m_nview.GetZDO().GetLong(ZDOVars.s_rodOwner, 0L); ZDOID zDOID = ((Character)player).GetZDOID(); return num == ((ZDOID)(ref zDOID)).UserID; } internal static void Plink() { try { FishCuesPlugin self = Self; if (!((Object)(object)self == (Object)null) && Enabled.Value && !(PlinkVolume.Value <= 0f)) { self.EnsureSources(); if ((Object)(object)self._plinkSource != (Object)null && (Object)(object)self._plinkSource.clip != (Object)null) { self._plinkSource.PlayOneShot(self._plinkSource.clip, Mathf.Clamp01(PlinkVolume.Value)); Log.LogDebug((object)"Bite plink."); } } } catch (Exception e) { WarnOnce("Plink", e); } } internal static void WarnOnce(string key, Exception e) { if (Warned.Add(key)) { Log.LogWarning((object)("FishCues: " + key + " failed (" + e.GetType().Name + ": " + e.Message + "). That cue is disabled; the game is unaffected.")); Log.LogDebug((object)e.ToString()); } } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }