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 StoneOvenAlarm v1.0.1
plugins/StoneOvenAlarm.dll
Decompiled 2 days agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Threading; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.Networking; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("StoneOvenAlarm")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.1.0")] [assembly: AssemblyInformationalVersion("1.0.1")] [assembly: AssemblyProduct("StoneOvenAlarm")] [assembly: AssemblyTitle("StoneOvenAlarm")] [assembly: AssemblyVersion("1.0.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 StoneOvenAlarm { internal class AlarmPlayer : MonoBehaviour { private const string StoneOvenPrefab = "piece_oven"; private static readonly Dictionary<SoundPreset, string> GameSounds = new Dictionary<SoundPreset, string> { { SoundPreset.MagicBells, "sfx_secretfound" }, { SoundPreset.Forge, "sfx_gui_craftitem_forge_end" }, { SoundPreset.CraftDone, "sfx_gui_craftitem_end" }, { SoundPreset.OvenDone, "sfx_oven_done" }, { SoundPreset.Hugin, "fx_open_catbox" } }; private readonly Dictionary<SoundPreset, AudioClip> _gameClips = new Dictionary<SoundPreset, AudioClip>(); private AudioSource _source; private AudioClip _defaultClip; private AudioClip _customClip; private Coroutine _playing; private void Awake() { _source = ((Component)this).gameObject.AddComponent<AudioSource>(); _source.playOnAwake = false; _source.spatialBlend = 0f; _source.ignoreListenerPause = true; _defaultClip = CreateDefaultChime(); LoadCustomClip(); Plugin.SoundFile.SettingChanged += delegate { if (!string.IsNullOrWhiteSpace(Plugin.SoundFile.Value)) { Plugin.SoundPresetSetting.Value = SoundPreset.Custom; } LoadCustomClip(); }; } private void Update() { if (FilePicker.TryTakeResult(out var path)) { Plugin.SoundFile.Value = path; } } public void OnFoodDone(CookingStation station, string itemPrefabName) { //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.Enabled.Value || _playing != null) { return; } Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null) && (!Plugin.StoneOvenOnly.Value || !(Utils.GetPrefabName(((Component)station).gameObject) != "piece_oven")) && !(Vector3.Distance(((Component)localPlayer).transform.position, ((Component)station).transform.position) > Plugin.MaxDistance.Value)) { if (Plugin.ShowMessage.Value && (Object)(object)MessageHud.instance != (Object)null) { MessageHud.instance.ShowMessage((MessageType)2, BuildMessage(station, itemPrefabName), 0, (Sprite)null, false, true); } Play(); } } public void Play() { if (_playing != null) { ((MonoBehaviour)this).StopCoroutine(_playing); } _source.Stop(); _playing = ((MonoBehaviour)this).StartCoroutine(PlayRepeated()); } private IEnumerator PlayRepeated() { AudioClip clip = GetClip(); int count = Mathf.Max(1, Plugin.RepeatCount.Value); for (int n = 0; n < count; n++) { _source.PlayOneShot(clip, Plugin.Volume.Value); if (n < count - 1) { yield return (object)new WaitForSecondsRealtime(clip.length + Plugin.RepeatInterval.Value); } } yield return (object)new WaitForSecondsRealtime(clip.length); _playing = null; } private AudioClip GetClip() { SoundPreset value = Plugin.SoundPresetSetting.Value; if (value == SoundPreset.Custom) { if (!((Object)(object)_customClip != (Object)null)) { return _defaultClip; } return _customClip; } if (GameSounds.TryGetValue(value, out var value2)) { if (!_gameClips.TryGetValue(value, out var value3) || (Object)(object)value3 == (Object)null) { value3 = FindGameClip(value2); if ((Object)(object)value3 != (Object)null) { _gameClips[value] = value3; } } if ((Object)(object)value3 != (Object)null) { return value3; } } return _defaultClip; } private static AudioClip FindGameClip(string prefabName) { if ((Object)(object)ZNetScene.instance == (Object)null) { return null; } GameObject prefab = ZNetScene.instance.GetPrefab(prefabName); ZSFX val = (((Object)(object)prefab != (Object)null) ? prefab.GetComponentInChildren<ZSFX>(true) : null); if ((Object)(object)val == (Object)null || val.m_audioClips == null || val.m_audioClips.Length == 0) { Plugin.Log.LogWarning((object)("Game sound '" + prefabName + "' not found. Using the built-in chime.")); return null; } return val.m_audioClips[0]; } private static string BuildMessage(CookingStation station, string itemPrefabName) { string newValue = itemPrefabName; GameObject val = (((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetItemPrefab(itemPrefabName) : null); ItemDrop val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<ItemDrop>() : null); if ((Object)(object)val2 != (Object)null) { newValue = val2.m_itemData.m_shared.m_name; } string text = Plugin.MessageText.Value; if (string.IsNullOrWhiteSpace(text)) { text = "Comida pronta!"; } text = text.Replace("{item}", newValue).Replace("{station}", station.m_name); if (Localization.instance == null) { return text; } return Localization.instance.Localize(text); } private void LoadCustomClip() { //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) _customClip = null; string text = Plugin.SoundFile.Value?.Trim().Trim('"'); if (string.IsNullOrEmpty(text)) { return; } string text2 = ResolveSoundPath(text); if (text2 == null) { Plugin.Log.LogWarning((object)("Sound file not found: '" + text + "'. Using the built-in chime.")); return; } AudioType type; switch (Path.GetExtension(text2).ToLowerInvariant()) { case ".wav": type = (AudioType)20; break; case ".ogg": type = (AudioType)14; break; case ".mp3": type = (AudioType)13; break; default: Plugin.Log.LogWarning((object)("Unsupported sound format '" + text2 + "'. Use .wav, .ogg or .mp3.")); return; } ((MonoBehaviour)this).StartCoroutine(LoadClip(text2, type)); } private static string ResolveSoundPath(string setting) { if (Path.IsPathRooted(setting)) { if (!File.Exists(setting)) { return null; } return setting; } string directoryName = Path.GetDirectoryName(((BaseUnityPlugin)Plugin.Instance).Info.Location); string[] array = new string[3] { Paths.ConfigPath, directoryName, Paths.PluginPath }; foreach (string text in array) { if (!string.IsNullOrEmpty(text)) { string text2 = Path.Combine(text, setting); if (File.Exists(text2)) { return text2; } } } return null; } private IEnumerator LoadClip(string path, AudioType type) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) UnityWebRequest request = UnityWebRequestMultimedia.GetAudioClip(new Uri(path).AbsoluteUri, type); try { yield return request.SendWebRequest(); if ((int)request.result != 1) { Plugin.Log.LogWarning((object)("Failed to load sound '" + path + "': " + request.error)); yield break; } AudioClip content = DownloadHandlerAudioClip.GetContent(request); if ((Object)(object)content == (Object)null || content.length <= 0f) { Plugin.Log.LogWarning((object)("Sound '" + path + "' is empty or invalid.")); yield break; } ((Object)content).name = Path.GetFileNameWithoutExtension(path); _customClip = content; Plugin.Log.LogInfo((object)("Loaded custom alarm sound: " + path)); } finally { ((IDisposable)request)?.Dispose(); } } private static AudioClip CreateDefaultChime() { float[] array = new float[2] { 1318.51f, 1046.5f }; int num = 19845; float[] array2 = new float[num * array.Length]; for (int i = 0; i < array.Length; i++) { float num2 = array[i]; for (int j = 0; j < num; j++) { float num3 = (float)j / 44100f; float num4 = Mathf.Clamp01(num3 / 0.005f); float num5 = Mathf.Exp((0f - num3) * 7f); float num6 = Mathf.Sin(MathF.PI * 2f * num2 * num3) + 0.35f * Mathf.Sin(MathF.PI * 2f * num2 * 2.76f * num3) * Mathf.Exp((0f - num3) * 12f) + 0.2f * Mathf.Sin(MathF.PI * 2f * num2 * 5.4f * num3) * Mathf.Exp((0f - num3) * 20f); array2[i * num + j] = num6 * num4 * num5 * 0.5f; } } AudioClip obj = AudioClip.Create("StoneOvenAlarmChime", array2.Length, 1, 44100, false); obj.SetData(array2, 0); return obj; } } internal sealed class ConfigurationManagerAttributes { public int? Order; public bool? HideDefaultButton; public Action<ConfigEntryBase> CustomDrawer; } [HarmonyPatch(typeof(CookingStation), "SetSlotVisual")] internal static class CookingStationPatch { private const int StatusNotDone = 0; private const int StatusDone = 1; private const int Unknown = -1; private static readonly ConditionalWeakTable<CookingStation, int[]> LastStatus = new ConditionalWeakTable<CookingStation, int[]>(); private static void Postfix(CookingStation __instance, int i, string item, object[] __args) { int num = Convert.ToInt32(__args[3]); int[] value = LastStatus.GetValue(__instance, CreateSlots); if (i >= 0 && i < value.Length) { int num2 = value[i]; value[i] = num; if (num2 == 0 && num == 1 && !string.IsNullOrEmpty(item)) { Plugin.Alarm?.OnFoodDone(__instance, item); } } } private static int[] CreateSlots(CookingStation station) { Transform[] slots = station.m_slots; int[] array = new int[(slots != null) ? slots.Length : 0]; for (int i = 0; i < array.Length; i++) { array[i] = -1; } return array; } } internal static class FilePicker { [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] private class OpenFileName { public int lStructSize; public IntPtr hwndOwner; public IntPtr hInstance; public string lpstrFilter; public string lpstrCustomFilter; public int nMaxCustFilter; public int nFilterIndex; public string lpstrFile; public int nMaxFile; public string lpstrFileTitle; public int nMaxFileTitle; public string lpstrInitialDir; public string lpstrTitle; public int Flags; public short nFileOffset; public short nFileExtension; public string lpstrDefExt; public IntPtr lCustData; public IntPtr lpfnHook; public string lpTemplateName; public IntPtr pvReserved; public int dwReserved; public int FlagsEx; } private const int OFN_NOCHANGEDIR = 8; private const int OFN_PATHMUSTEXIST = 2048; private const int OFN_FILEMUSTEXIST = 4096; private const int OFN_EXPLORER = 524288; private static volatile bool _isOpen; private static volatile string _result; public static bool IsSupported { get { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Invalid comparison between Unknown and I4 //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Invalid comparison between Unknown and I4 if ((int)Application.platform != 2) { return (int)Application.platform == 7; } return true; } } public static bool IsOpen => _isOpen; public static void Open() { if (!_isOpen && IsSupported) { _isOpen = true; IntPtr owner = GetActiveWindow(); Thread thread = new Thread((ThreadStart)delegate { Run(owner); }); thread.IsBackground = true; thread.Name = "StoneOvenAlarm file picker"; thread.SetApartmentState(ApartmentState.STA); thread.Start(); } } public static bool TryTakeResult(out string path) { path = _result; if (path == null) { return false; } _result = null; return true; } private static void Run(IntPtr owner) { try { OpenFileName openFileName = new OpenFileName { hwndOwner = owner, lpstrFilter = "Audio files (*.wav, *.ogg, *.mp3)\0*.wav;*.ogg;*.mp3\0All files (*.*)\0*.*\0\0", lpstrFile = new string('\0', 1024), lpstrTitle = "Stone Oven Alarm - choose a sound", Flags = 530440 }; openFileName.nMaxFile = openFileName.lpstrFile.Length; openFileName.lStructSize = Marshal.SizeOf(openFileName); if (GetOpenFileNameW(openFileName)) { string text = openFileName.lpstrFile?.TrimEnd('\0'); if (!string.IsNullOrEmpty(text)) { _result = text; } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not open the file dialog: " + ex.Message)); } finally { _isOpen = false; } } [DllImport("user32.dll")] private static extern IntPtr GetActiveWindow(); [DllImport("comdlg32.dll", CharSet = CharSet.Unicode, SetLastError = true)] private static extern bool GetOpenFileNameW([In][Out] OpenFileName ofn); } [BepInPlugin("batera.StoneOvenAlarm", "StoneOvenAlarm", "1.0.1")] public class Plugin : BaseUnityPlugin { public const string PluginGuid = "batera.StoneOvenAlarm"; public const string PluginName = "StoneOvenAlarm"; public const string PluginVersion = "1.0.1"; internal static Plugin Instance; internal static ManualLogSource Log; internal static AlarmPlayer Alarm; internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<bool> StoneOvenOnly; internal static ConfigEntry<float> MaxDistance; internal static ConfigEntry<SoundPreset> SoundPresetSetting; internal static ConfigEntry<string> SoundFile; internal static ConfigEntry<int> RepeatCount; internal static ConfigEntry<float> RepeatInterval; internal static ConfigEntry<float> Volume; internal static ConfigEntry<bool> ShowMessage; internal static ConfigEntry<string> MessageText; private Harmony _harmony; private void Awake() { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; BindConfig(); GameObject val = new GameObject("StoneOvenAlarm"); Object.DontDestroyOnLoad((Object)val); Alarm = val.AddComponent<AlarmPlayer>(); _harmony = new Harmony("batera.StoneOvenAlarm"); _harmony.PatchAll(typeof(Plugin).Assembly); ((BaseUnityPlugin)this).Logger.LogInfo((object)"StoneOvenAlarm 1.0.1 loaded"); } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } private void BindConfig() { //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Expected O, but got Unknown //IL_0215: Unknown result type (might be due to invalid IL or missing references) //IL_021f: Expected O, but got Unknown Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "Enabled", true, Desc("Enable or disable the alarm.", 3)); StoneOvenOnly = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "StoneOvenOnly", true, Desc("If true, only the Stone Oven triggers the alarm. If false, cooking stations (spits, iron cooking station) also do.", 2)); MaxDistance = ((BaseUnityPlugin)this).Config.Bind<float>("1 - General", "MaxDistance", 60f, Desc("Only play the alarm if you are within this distance (meters) of the station.", 1, (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 200f))); Volume = ((BaseUnityPlugin)this).Config.Bind<float>("2 - Sound", "Volume", 0.6f, Desc("Alarm volume.", 6, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f))); RepeatCount = ((BaseUnityPlugin)this).Config.Bind<int>("2 - Sound", "RepeatCount", 1, Desc("How many times the sound plays.", 5, (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 20))); RepeatInterval = ((BaseUnityPlugin)this).Config.Bind<float>("2 - Sound", "RepeatInterval", 0.4f, Desc("Pause in seconds between each repetition.", 4, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f))); SoundPresetSetting = ((BaseUnityPlugin)this).Config.Bind<SoundPreset>("2 - Sound", "SoundPreset", SoundPreset.MagicBells, Desc("Alarm sound. Chime is built into the mod; MagicBells, Forge, CraftDone, OvenDone and Hugin are game sounds; Custom uses SoundFile.", 7)); SoundFile = ((BaseUnityPlugin)this).Config.Bind<string>("2 - Sound", "SoundFile", "", new ConfigDescription("Custom sound file (.wav, .ogg or .mp3), used when SoundPreset = Custom. Click Browse to pick a file, or Reset to clear it. In the .cfg you can also type an absolute path or a file name inside the BepInEx/config folder.", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 3, CustomDrawer = DrawSoundFile } })); ((BaseUnityPlugin)this).Config.Bind<string>("2 - Sound", "TestAlarm", "", new ConfigDescription("Plays the alarm with the current settings.", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 2, HideDefaultButton = true, CustomDrawer = DrawTestButton } })); ShowMessage = ((BaseUnityPlugin)this).Config.Bind<bool>("3 - Message", "ShowMessage", true, Desc("Also show a message in the center of the screen.", 2)); MessageText = ((BaseUnityPlugin)this).Config.Bind<string>("3 - Message", "MessageText", "Comida pronta!", Desc("Message shown when the food is done. {item} = food name, {station} = station name.", 1)); } private static ConfigDescription Desc(string text, int order, AcceptableValueBase range = null) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown return new ConfigDescription(text, range, new object[1] { new ConfigurationManagerAttributes { Order = order } }); } private static void DrawSoundFile(ConfigEntryBase entry) { string value = SoundFile.Value; GUILayout.Label(string.IsNullOrEmpty(value) ? "(none)" : Path.GetFileName(value), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); if (FilePicker.IsSupported) { bool enabled = GUI.enabled; GUI.enabled = enabled && !FilePicker.IsOpen; if (GUILayout.Button(FilePicker.IsOpen ? "Choosing..." : "Browse...", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) })) { FilePicker.Open(); } GUI.enabled = enabled; } } private static void DrawTestButton(ConfigEntryBase entry) { if (GUILayout.Button("Test alarm", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) })) { Alarm?.Play(); } } } public enum SoundPreset { Chime, MagicBells, Forge, CraftDone, OvenDone, Hugin, Custom } }