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 YourOwnPersonalBoombox v1.4.4
PersonalBoombox.dll
Decompiled 2 years 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.InteropServices; using System.Runtime.Serialization.Formatters.Binary; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using GameNetcodeStuff; using HarmonyLib; using PersonalBoombox.Patches; using TMPro; using Unity.Collections; using Unity.Netcode; using UnityEngine; using UnityEngine.Events; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; using UnityEngine.Networking; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("PersonalBoombox")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("PersonalBoombox")] [assembly: AssemblyCopyright("Copyright © 2023")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("90672a7b-b084-4f8d-9d78-46affb7b97e7")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.7.1", FrameworkDisplayName = ".NET Framework 4.7.1")] [assembly: AssemblyVersion("1.0.0.0")] namespace PersonalBoombox { public static class Assets { [Serializable] private class PBDataJson { public string name; public string description; public int price; public float volume; public int red; public int blue; public int green; public Color color; public string feedback; public static PBDataJson GetData(string path) { if (File.Exists(path)) { try { string json = File.ReadAllText(path); PBDataJson pBDataJson = CreateFromJson(json); pBDataJson.FillMissingValues(); pBDataJson.AdjustValues(); pBDataJson.feedback = "Loaded data.json file"; return pBDataJson; } catch { return GetDefault("Error reading data.json file".CreateError()); } } return GetDefault("Missing data.json file".CreateWarning()); } public static PBDataJson GetDefault(string feedback) { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) PBDataJson pBDataJson = new PBDataJson(); pBDataJson.feedback = feedback; pBDataJson.name = "Untitled"; pBDataJson.description = "Missing Description"; pBDataJson.price = 60; pBDataJson.volume = 0.4f; pBDataJson.red = 255; pBDataJson.blue = 255; pBDataJson.green = 255; pBDataJson.color = Color.white; return pBDataJson; } private void FillMissingValues() { if (string.IsNullOrWhiteSpace(name)) { name = "Untitled"; } if (string.IsNullOrWhiteSpace(description)) { description = "Missing Description"; } } private void AdjustValues() { //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) int length = Mathf.Min(name.Length, 20); name = name.Substring(0, length); price = Mathf.Clamp(price, 5, 1000); volume = Mathf.Clamp(volume, 0.1f, SyncedInstance<PluginConfig>.Default.maxVolumeValue); red = Mathf.Clamp(red, 0, 255); blue = Mathf.Clamp(blue, 0, 255); green = Mathf.Clamp(green, 0, 255); color = new Color((float)red / 255f, (float)green / 255f, (float)blue / 255f, 1f); } private static PBDataJson CreateFromJson(string json) { return JsonUtility.FromJson<PBDataJson>(json); } public string ToJson() { return JsonUtility.ToJson((object)this); } } private const string mainAssetBundleName = "personalboombox"; public const int prefabCopyCount = 15; public static AssetBundle MainAssetBundle = null; public static GameObject originalPrefab; public static GameObject[] prefabCopies; public static GameObject canvasPrefab; public static GameObject volumeControlCanvasPrefab; public static Sprite boomboxSprite; public static int LimitExceedCounter = 0; public static int BoomboxesLoading = 0; private static readonly string[] musicExts = new string[3] { "mp3", "ogg", "wav" }; private static ManualLogSource logger => PersonalBoomboxPlugin.Instance.logger; private static string GetAssemblyName() { return Assembly.GetExecutingAssembly().FullName.Split(new char[1] { ',' })[0]; } public static void LoadAssetBundle() { if ((Object)(object)MainAssetBundle == (Object)null) { using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(GetAssemblyName() + ".personalboombox"); MainAssetBundle = AssetBundle.LoadFromStream(stream); } originalPrefab = Assets.Load<GameObject>("prefab", onlyReportErrors: true); prefabCopies = (GameObject[])(object)new GameObject[15]; for (int i = 0; i < 15; i++) { GameObject val = Assets.Load<GameObject>($"prefab{i + 1}", onlyReportErrors: false); prefabCopies[i] = val; } canvasPrefab = Assets.Load<GameObject>("debugCanvas", onlyReportErrors: true); volumeControlCanvasPrefab = Assets.Load<GameObject>("volumeControlCanvas", onlyReportErrors: true); boomboxSprite = Assets.Load<Sprite>("boomboxIcon", onlyReportErrors: true); } public static T Load<T>(string name, bool onlyReportErrors = true) where T : Object { if ((Object)(object)MainAssetBundle == (Object)null) { logger.LogError((object)"Trying to load in asset but asset bundle is missing"); return default(T); } if (onlyReportErrors) { logger.LogInfo((object)("Loading asset " + name)); } return MainAssetBundle.LoadAsset<T>(name); } public static void LoadMusicFolders() { List<PersonalBoomboxPlugin.RequestData> requests = PersonalBoomboxPlugin.requests; List<PersonalBoomboxPlugin.PBData> list = new List<PersonalBoomboxPlugin.PBData>(); int num = 0; foreach (PersonalBoomboxPlugin.RequestData item in requests) { string path = item.path; string[] directories = Directory.GetDirectories(path); string[] array = directories; foreach (string directory in array) { logger.LogInfo((object)("Reading from " + path + "!")); try { CreateFromDirectory(directory, list, num++, item); } catch (Exception ex) { logger.LogError((object)("Error loading at " + path)); logger.LogError((object)ex.ToString()); } } } list.Sort((PersonalBoomboxPlugin.PBData first, PersonalBoomboxPlugin.PBData second) => first.name.CompareTo(second.name)); num = 0; foreach (PersonalBoomboxPlugin.PBData item2 in list) { char c = (char)(65 + num); item2.prefixName = $"pb{c}"; item2.boomboxName = item2.prefixName + " " + item2.name; ((Object)item2.prefab).name = item2.boomboxName; num++; } PersonalBoomboxPlugin.data = list; } private static void CreateFromDirectory(string directory, List<PersonalBoomboxPlugin.PBData> data, int index, PersonalBoomboxPlugin.RequestData request) { //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) if (index >= 15) { logger.LogWarning((object)$"Loading too many boomboxes. There is a limit of {15}"); LimitExceedCounter++; return; } string path = Path.Combine(directory, "data.json"); IEnumerable<string> enumerable = from file in Directory.EnumerateFiles(directory, "*.*") where musicExts.Any((string x) => file.EndsWith(x, StringComparison.InvariantCultureIgnoreCase)) select file; string path2 = Path.Combine(directory, "decal.png"); PBDataJson data2 = PBDataJson.GetData(path); char c = (char)(97 + index); PersonalBoomboxPlugin.PBData pBData = new PersonalBoomboxPlugin.PBData(); pBData.request = request; pBData.feedback = new PersonalBoomboxPlugin.PBData.Feedback(); pBData.feedback.AddToMain(data2.feedback); pBData.name = data2.name; pBData.description = data2.description; pBData.price = data2.price; pBData.volume = data2.volume; pBData.prefab = prefabCopies[index]; pBData.script = pBData.prefab.GetComponent<PersonalBoomboxItem>(); pBData.script.musicAudios = new List<AudioClip>(); pBData.color = data2.color; pBData.icon = CreateSprite(data2.color); List<Coroutine> list = new List<Coroutine>(); foreach (string item3 in enumerable) { Coroutine item = MyOwnCoroutine.AddCoroutine(LoadAudioClip(pBData, item3)); list.Add(item); } if (File.Exists(path2)) { Coroutine item2 = MyOwnCoroutine.AddCoroutine(LoadTexture2D(pBData, path2)); list.Add(item2); } else { pBData.feedback.AddToMain("Missing decal.png"); } MyOwnCoroutine.AddCoroutine(WaitForDownloads(pBData, list)); data.Add(pBData); } private static IEnumerator WaitForDownloads(PersonalBoomboxPlugin.PBData data, List<Coroutine> coroutines) { BoomboxesLoading++; yield return null; int i = 0; foreach (Coroutine coroutine in coroutines) { yield return coroutine; i++; } PersonalBoomboxItem script = data.script; int count = script.musicAudios.Count; script.SortAudioClips(); script.finishedLoading = true; logger.LogInfo((object)$"{data.boomboxName} is fully set up with {count} songs!"); if (count == 0) { logger.LogWarning((object)"Boomboxes with 0 songs will be skipped in the initialization process"); } BoomboxesLoading--; } private static IEnumerator LoadTexture2D(PersonalBoomboxPlugin.PBData data, string path) { UnityWebRequest loader = UnityWebRequestTexture.GetTexture(path); loader.SendWebRequest(); while (!loader.isDone) { yield return null; } if (loader.error != null) { string errorString2 = "Error loading decal.png. Download failed"; logger.LogError((object)errorString2); logger.LogError((object)loader.error); data.feedback.AddToMain(errorString2.CreateError()); yield break; } Texture2D texture = DownloadHandlerTexture.GetContent(loader); if ((Object)(object)texture == (Object)null) { string errorString = "Error loading decal.png. File is weird"; data.feedback.AddToMain(errorString.CreateError()); logger.LogError((object)errorString); yield break; } ((Texture)texture).filterMode = (FilterMode)0; data.decal = texture; data.decalScale = GetDecalRatio(texture); string loadString = "Loaded decal.png"; logger.LogInfo((object)loadString); data.feedback.AddToMain(loadString); } private static IEnumerator LoadAudioClip(PersonalBoomboxPlugin.PBData data, string path) { string fileName = Path.GetFileName(path); AudioType fileType = GetAudioType(path); if ((int)fileType == 0) { string errorString3 = "Error loading " + fileName + ". Unsupported file ext."; logger.LogError((object)errorString3); data.feedback.AddToSongs(errorString3.CreateError()); yield break; } UnityWebRequest loader = UnityWebRequestMultimedia.GetAudioClip(path, GetAudioType(path)); DownloadHandler downloadHandler = loader.downloadHandler; DownloadHandlerAudioClip handler = (DownloadHandlerAudioClip)(object)((downloadHandler is DownloadHandlerAudioClip) ? downloadHandler : null); handler.streamAudio = true; yield return loader.SendWebRequest(); while (!loader.isDone) { yield return null; } if (loader.error != null) { string errorString2 = "Error loading " + fileName + ". Download failed"; logger.LogError((object)errorString2); logger.LogError((object)loader.error); data.feedback.AddToSongs(errorString2.CreateError()); yield break; } AudioClip clip = DownloadHandlerAudioClip.GetContent(loader); if ((Object)(object)clip == (Object)null || (int)clip.loadState != 2) { string errorString = "Error loading " + fileName + ". File is weird"; logger.LogError((object)errorString); data.feedback.AddToSongs(errorString.CreateError()); } else { data.script.AddAudioClip(clip); string loadString = "Loaded " + fileName; logger.LogInfo((object)loadString); data.feedback.AddToSongs(loadString); } } public static AudioType GetAudioType(string path) { //IL_003c: 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) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) return (AudioType)(Path.GetExtension(path).ToLowerInvariant() switch { ".mp3" => 13, ".ogg" => 14, ".wav" => 20, _ => 0, }); } public static Vector3 GetDecalRatio(Texture2D texture) { //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) float num = (float)((Texture)texture).width / (float)((Texture)texture).height; float num2; float num3; if (num > 2.1014493f) { num2 = 1f; num3 = 2.1014493f / num; } else if (num < 2.1014493f) { num2 = num / 2.1014493f; num3 = 1f; } else { num2 = 1f; num3 = 1f; } return new Vector3(num2, num3, 1f); } public static Sprite CreateSprite(Color color) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Expected O, but got Unknown //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: 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_0041: 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_0071: Unknown result type (might be due to invalid IL or missing references) Sprite val = boomboxSprite; Texture2D texture = val.texture; Texture2D val2 = new Texture2D(((Texture)texture).width, ((Texture)texture).height); Color[] pixels = texture.GetPixels(); for (int i = 0; i < pixels.Length; i++) { ref Color reference = ref pixels[i]; reference *= color; } val2.SetPixels(pixels); val2.Apply(); return Sprite.Create(val2, val.rect, val.pivot, val.pixelsPerUnit); } } [Serializable] public class PluginConfig : SyncedInstance<PluginConfig> { public static ConfigEntry<int> maxVolumeConfig; public static ConfigEntry<bool> pocketCarryConfig; public static ConfigEntry<int> batteryConfig; public bool pocketCarryValue; public int batteryValue; public float maxVolumeValue => (float)maxVolumeConfig.Value * 0.01f; public PluginConfig(ConfigFile cfg) { InitInstance(this); maxVolumeConfig = ConfigBindClamp("General", "Max Volume", 100, "Max possible volume value for all p. boomboxes. From 10 to 100.", 10, 100); pocketCarryConfig = cfg.Bind<bool>("Network General", "Pocket Play", false, "If true, lets the p. boombox keep playing when it's put in the inventory"); batteryConfig = ConfigBindClamp("Network General", "Battery Charge", 350, "The battery charge (in seconds) for all p. boomboxes. Clamped from 30 to 720", 30, 720); pocketCarryValue = pocketCarryConfig.Value; batteryValue = batteryConfig.Value; ConfigEntry<int> ConfigBindClamp(string section, string key, int defaultValue, string description, int min, int max) { ConfigEntry<int> val = cfg.Bind<int>(section, key, defaultValue, description); val.Value = Mathf.Clamp(val.Value, min, max); return val; } } public static void RequestSync() { //IL_002a: Unknown result type (might be due to invalid IL or missing references) if (!SyncedInstance<PluginConfig>.IsClient) { return; } FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(SyncedInstance<PluginConfig>.IntSize, (Allocator)2, -1); try { SyncedInstance<PluginConfig>.MessageManager.SendNamedMessage("PBoombox_OnRequestConfigSync", 0uL, val, (NetworkDelivery)3); } finally { ((IDisposable)(FastBufferWriter)(ref val)).Dispose(); } } public static void OnRequestSync(ulong clientId, FastBufferReader _) { //IL_0054: 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) //IL_0078: Unknown result type (might be due to invalid IL or missing references) if (!SyncedInstance<PluginConfig>.IsHost) { return; } SyncedInstance<PluginConfig>.logger.LogInfo((object)$"Config sync request received from client: {clientId}"); byte[] array = SyncedInstance<PluginConfig>.SerializeToBytes(SyncedInstance<PluginConfig>.Instance); int num = array.Length; FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(num + SyncedInstance<PluginConfig>.IntSize, (Allocator)2, -1); try { ((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref num, default(ForPrimitives)); ((FastBufferWriter)(ref val)).WriteBytesSafe(array, -1, 0); SyncedInstance<PluginConfig>.MessageManager.SendNamedMessage("PBoombox_OnReceieveConfigSync", clientId, val, (NetworkDelivery)3); } catch (Exception arg) { SyncedInstance<PluginConfig>.logger.LogError((object)$"Error occurred syncing config with client: {clientId}\n{arg}"); } finally { ((IDisposable)(FastBufferWriter)(ref val)).Dispose(); } } public static void OnReceiveSync(ulong _, FastBufferReader reader) { //IL_002d: 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 (!((FastBufferReader)(ref reader)).TryBeginRead(SyncedInstance<PluginConfig>.IntSize)) { SyncedInstance<PluginConfig>.logger.LogError((object)"Config sync error: Could not begin reading buffer."); return; } int num = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref num, default(ForPrimitives)); if (!((FastBufferReader)(ref reader)).TryBeginRead(num)) { SyncedInstance<PluginConfig>.logger.LogError((object)"Config sync error: Host could not sync."); return; } byte[] data = new byte[num]; ((FastBufferReader)(ref reader)).ReadBytesSafe(ref data, num, 0); SyncedInstance<PluginConfig>.SyncInstance(data); SyncedInstance<PluginConfig>.logger.LogInfo((object)"Successfully synced config with host."); } [HarmonyPatch(typeof(PlayerControllerB), "ConnectClientToPlayerObject")] [HarmonyPostfix] public static void InitializeLocalPlayer() { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Expected O, but got Unknown //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown if (SyncedInstance<PluginConfig>.IsHost) { SyncedInstance<PluginConfig>.MessageManager.RegisterNamedMessageHandler("PBoombox_OnRequestConfigSync", new HandleNamedMessageDelegate(OnRequestSync)); SyncedInstance<PluginConfig>.Synced = true; } else { SyncedInstance<PluginConfig>.Synced = false; SyncedInstance<PluginConfig>.MessageManager.RegisterNamedMessageHandler("PBoombox_OnReceieveConfigSync", new HandleNamedMessageDelegate(OnReceiveSync)); RequestSync(); } } [HarmonyPatch(typeof(GameNetworkManager), "StartDisconnect")] public static void PlayerLeave() { SyncedInstance<PluginConfig>.RevertSync(); } } [Serializable] public class SyncedInstance<T> { [NonSerialized] protected static int IntSize = 4; internal static CustomMessagingManager MessageManager => NetworkManager.Singleton.CustomMessagingManager; internal static bool IsClient => NetworkManager.Singleton.IsClient; internal static bool IsHost => NetworkManager.Singleton.IsHost; internal static ManualLogSource logger => PersonalBoomboxPlugin.Instance.logger; public static T Default { get; private set; } public static T Instance { get; private set; } public static bool Synced { get; internal set; } protected void InitInstance(T instance) { Default = instance; Instance = instance; IntSize = 4; } internal static void SyncInstance(byte[] data) { Instance = DeserializeFromBytes(data); Synced = true; } internal static void RevertSync() { Instance = Default; Synced = false; } public static byte[] SerializeToBytes(T val) { BinaryFormatter binaryFormatter = new BinaryFormatter(); using MemoryStream memoryStream = new MemoryStream(); try { binaryFormatter.Serialize(memoryStream, val); return memoryStream.ToArray(); } catch (Exception arg) { logger.LogError((object)$"Error serializing instance: {arg}"); return null; } } public static T DeserializeFromBytes(byte[] data) { BinaryFormatter binaryFormatter = new BinaryFormatter(); using MemoryStream serializationStream = new MemoryStream(data); try { return (T)binaryFormatter.Deserialize(serializationStream); } catch (Exception arg) { logger.LogError((object)$"Error deserializing instance: {arg}"); return default(T); } } } public class VolumeControl : MonoBehaviour { public float volumeModifier; private int volumeValue; public Image fillImage; public Image[] targetImages; private const float fillMin = 0.12f; private const float fillMax = 0.88f; private const float displayTime = 3f; private const float fadeTime = 0.25f; private Coroutine displayC; public static VolumeControl Instance { get; private set; } private void Awake() { //IL_003e: Unknown result type (might be due to invalid IL or missing references) Instance = this; volumeValue = PlayerPrefs.GetInt("PBoombox_VolumeControlValue", 100); volumeModifier = (float)volumeValue * 0.01f; Image[] array = targetImages; foreach (Image val in array) { ((Graphic)val).color = Color.clear; } } private void Update() { int num = 0; if (IfKeyPress(Keyboard.current.minusKey, Keyboard.current.numpadMinusKey)) { num = -1; } else if (IfKeyPress(Keyboard.current.equalsKey, Keyboard.current.numpadPlusKey)) { num = 1; } if (num != 0) { volumeValue = Mathf.Clamp(volumeValue + num * 10, 10, 100); volumeModifier = (float)volumeValue * 0.01f; PlayerPrefs.SetInt("PBoombox_VolumeControlValue", volumeValue); fillImage.fillAmount = Mathf.Lerp(0.12f, 0.88f, volumeModifier); Display(); } } private bool IfKeyPress(params KeyControl[] keys) { foreach (KeyControl val in keys) { if (((ButtonControl)val).wasPressedThisFrame) { return true; } } return false; } private void Display() { if (displayC != null) { ((MonoBehaviour)this).StopCoroutine(displayC); } displayC = ((MonoBehaviour)this).StartCoroutine(DisplayCoroutine()); } private IEnumerator DisplayCoroutine() { Image[] array = targetImages; foreach (Image i in array) { ((Graphic)i).color = Color.white; } yield return (object)new WaitForSeconds(3f); Image[] array2 = targetImages; foreach (Image j in array2) { ((Graphic)j).color = Color.clear; } displayC = null; } } public class PersonalBoomboxItem : GrabbableObject { public AudioSource boomboxAudio; public List<AudioClip> musicAudios; public bool finishedLoading; public AudioClip[] stopAudios; public Random musicRandomizer; private StartOfRound playersManager; private RoundManager roundManager; public bool isPlayingMusic; private float noiseInterval; private int timesPlayedWithoutTurningOff; public float baseAudio; public bool continuePlayingOnPocket; public int AudioClipCount => (musicAudios != null) ? musicAudios.Count : 0; public void AddAudioClip(AudioClip clip) { musicAudios.Add(clip); } public void SortAudioClips() { musicAudios.Sort((AudioClip first, AudioClip second) => ((Object)first).name.CompareTo(((Object)second).name)); } public override void Start() { ((GrabbableObject)this).Start(); playersManager = Object.FindObjectOfType<StartOfRound>(); roundManager = Object.FindObjectOfType<RoundManager>(); musicRandomizer = new Random(playersManager.randomMapSeed - 10); baseAudio = boomboxAudio.volume; HUDManager.Instance.DisplayTip("P. Boombox Volume Control", "Press +/- to change the volume of all personal boomboxes", false, true, "PBoombox_VolumeControl"); } public override void ItemActivate(bool used, bool buttonDown = true) { ((GrabbableObject)this).ItemActivate(used, buttonDown); StartMusic(used); } private void StartMusic(bool startMusic, bool pitchDown = false) { if (startMusic && musicAudios.Count > 0) { boomboxAudio.clip = musicAudios[musicRandomizer.Next(0, musicAudios.Count)]; boomboxAudio.pitch = 1f; boomboxAudio.Play(); } else if (isPlayingMusic) { if (pitchDown) { ((MonoBehaviour)this).StartCoroutine(musicPitchDown()); } else { boomboxAudio.Stop(); boomboxAudio.PlayOneShot(stopAudios[Random.Range(0, stopAudios.Length)]); } timesPlayedWithoutTurningOff = 0; } base.isBeingUsed = startMusic; isPlayingMusic = startMusic; } private IEnumerator musicPitchDown() { for (int i = 0; i < 30; i++) { yield return null; AudioSource obj = boomboxAudio; obj.pitch -= 0.033f; if (boomboxAudio.pitch <= 0f) { break; } } boomboxAudio.Stop(); boomboxAudio.PlayOneShot(stopAudios[Random.Range(0, stopAudios.Length)]); } public override void UseUpBatteries() { ((GrabbableObject)this).UseUpBatteries(); StartMusic(startMusic: false, pitchDown: true); } public override void PocketItem() { ((GrabbableObject)this).PocketItem(); if (!continuePlayingOnPocket) { StartMusic(startMusic: false); } } public override void Update() { //IL_006d: Unknown result type (might be due to invalid IL or missing references) ((GrabbableObject)this).Update(); if (isPlayingMusic) { boomboxAudio.volume = baseAudio * VolumeControl.Instance.volumeModifier; if (noiseInterval <= 0f) { noiseInterval = 1f; timesPlayedWithoutTurningOff++; roundManager.PlayAudibleNoise(((Component)this).transform.position, 16f, 0.9f, timesPlayedWithoutTurningOff, false, 5); } else { noiseInterval -= Time.deltaTime; } if (base.insertedBattery.charge < 0.05f) { boomboxAudio.pitch = 1f - (0.05f - base.insertedBattery.charge) * 4f; } } } } [BepInPlugin("ImoutoSama.PersonalBoombox", "Personal Boombox", "1.4.4")] [BepInProcess("Lethal Company.exe")] public class PersonalBoomboxPlugin : BaseUnityPlugin { public class PBData { public class Feedback { private List<string> main; private List<string> songs; public Feedback() { main = new List<string>(); songs = new List<string>(); } public void AddToMain(string text) { main.Add(text); } public void AddToSongs(string text) { songs.Add(text); } public string GetMainReport() { return string.Join("\n", main); } public string GetSongsReport() { string empty = string.Empty; if (songs.Count == 0) { empty += "Found 0 songs".CreateWarning(); } else { string arg = ((songs.Count != 1) ? "s" : string.Empty); empty += $"{songs.Count} song{arg}"; foreach (string song in songs) { empty = empty + "\n" + song; } } return empty; } } public RequestData request; public Feedback feedback; public string name; public string prefixName; public string boomboxName; public string description; public int price; public float volume; public Color color; public GameObject prefab; public PersonalBoomboxItem script; public Sprite icon; public Item item; public int itemIndex; public Texture2D decal; public Vector3 decalScale; public bool addedToTerminalKeyword; public bool IsFinished => Object.op_Implicit((Object)(object)script) && script.finishedLoading; public bool IsValid => Object.op_Implicit((Object)(object)script) && script.AudioClipCount > 0; public int GetPrice => request.overridePriceFlag ? request.overridePriceValue : price; } public class RequestData { public string path; public bool overridePriceFlag; public int overridePriceValue; public RequestData(string path) { this.path = path; overridePriceFlag = false; overridePriceValue = 0; } public RequestData(string path, bool overridePrice, int overridePriceValue) { this.path = path; overridePriceFlag = overridePrice; this.overridePriceValue = overridePriceValue; } } private const string modGUID = "ImoutoSama.PersonalBoombox"; private const string modName = "Personal Boombox"; private const string modVersion = "1.4.4"; private readonly Harmony harmony = new Harmony("ImoutoSama.PersonalBoombox"); internal ManualLogSource logger; public static List<PBData> data; public static List<RequestData> requests; public static PersonalBoomboxPlugin Instance { get; private set; } public static PluginConfig MyConfig { get; internal set; } private void Awake() { if ((Object)(object)Instance == (Object)null) { Instance = this; } logger = Logger.CreateLogSource("ImoutoSama.PersonalBoombox"); logger.LogInfo((object)"Plugin Personal Boombox has been added!"); MyConfig = new PluginConfig(((BaseUnityPlugin)this).Config); harmony.PatchAll(typeof(TerminalPatch)); harmony.PatchAll(typeof(StartOfRoundPatch)); harmony.PatchAll(typeof(GameNetworkManagerPatch)); harmony.PatchAll(typeof(MenuManagerPatch)); harmony.PatchAll(typeof(PluginConfig)); data = new List<PBData>(); requests = new List<RequestData>(); Assets.LoadAssetBundle(); AddDirectory(GetLazyPath()); } public string GetLazyPath() { string text = Path.Combine(Paths.PluginPath, "Your Own Personal Boomboxes"); if (!Directory.Exists(text)) { Directory.CreateDirectory(text); Instance.logger.LogInfo((object)"Creaing 'Your Own Personal Boomboxes' folder in the plugin directory"); CopyExampleZIP(text); } return text; } public void CopyExampleZIP(string targetPath) { string location = Assembly.GetExecutingAssembly().Location; string directoryName = Path.GetDirectoryName(location); string text = Path.Combine(directoryName, "EXAMPLE.zip"); if (!File.Exists(text)) { Instance.logger.LogError((object)"Tried to copy over EXAMPLE.zip file but could not find it"); return; } string destFileName = Path.Combine(targetPath, "EXAMPLE.zip"); File.Copy(text, destFileName); } public static bool ContainsPath(string path) { foreach (RequestData request in requests) { if (request.path == path) { return true; } } return false; } public static RequestData AddFromAssemblyDll(string dllPath) { string directoryName = Path.GetDirectoryName(dllPath); return AddDirectory(directoryName); } public static RequestData AddDirectory(string path) { if (ContainsPath(path)) { Instance.logger.LogWarning((object)("Trying to add boombox data path that's already been added: " + path)); return null; } Instance.logger.LogInfo((object)("Adding path to read boombox data from: " + path)); RequestData requestData = new RequestData(path); requests.Add(requestData); return requestData; } public static string GetBonusReport() { if (data == null) { return "Data variable is null. This should literally never happen?!".CreateError(); } if (data.Count == 0) { return "No boomboxes were loaded".CreateWarning(); } int limitExceedCounter = Assets.LimitExceedCounter; if (limitExceedCounter > 0) { return $"Ignored {limitExceedCounter} boomboxes as they exceed the limit of {15}".CreateWarning(); } return "Everything seems good"; } } public static class StringUtilities { public static string CreateWarning(this string text) { return "<color=yellow>" + text + "</color>"; } public static string CreateError(this string text) { return "<color=red>" + text + "</color>"; } } public class MyOwnCoroutine : MonoBehaviour { private static MyOwnCoroutine _Instance; public static MyOwnCoroutine Instance { get { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown if ((Object)(object)_Instance == (Object)null) { GameObject val = new GameObject("My Own Coroutine"); _Instance = val.AddComponent<MyOwnCoroutine>(); Object.DontDestroyOnLoad((Object)(object)val); } return _Instance; } } public static Coroutine AddCoroutine(IEnumerator c) { return ((MonoBehaviour)Instance).StartCoroutine(c); } } } namespace PersonalBoombox.Patches { [HarmonyPatch(typeof(GameNetworkManager))] internal class GameNetworkManagerPatch { [HarmonyPatch("Start")] [HarmonyPostfix] public static void StartPatch(ref GameNetworkManager __instance) { PersonalBoomboxPlugin.Instance.logger.LogInfo((object)"Adding personal boomboxes to network list"); NetworkManager component = ((Component)__instance).GetComponent<NetworkManager>(); GameObject[] prefabCopies = Assets.prefabCopies; foreach (GameObject val in prefabCopies) { component.AddNetworkPrefab(val); } } } [HarmonyPatch(typeof(MenuManager))] internal class MenuManagerPatch { public static bool done; public static GameObject contentGameObject; public static Transform contentParent; public static GameObject blockPrefab; public static GameObject spacingPrefab; public static TextMeshProUGUI errorsTextMesh; public static bool loadedReport; [HarmonyPatch("Awake")] [HarmonyPostfix] private static void AwakePatch(ref MenuManager __instance) { AddReportDisplay(ref __instance); if (!done) { done = true; Assets.LoadMusicFolders(); } } private static void AddReportDisplay(ref MenuManager __instance) { //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Expected O, but got Unknown try { Transform parent = ((Component)__instance).transform.parent; Transform val = parent.Find("MenuContainer").Find("MainButtons"); GameObject gameObject = ((Component)val.Find("HostButton")).gameObject; GameObject val2 = Object.Instantiate<GameObject>(Assets.canvasPrefab); Button component = ((Component)val2.transform.Find("Button")).GetComponent<Button>(); ((UnityEventBase)component.onClick).RemoveAllListeners(); ((UnityEvent)component.onClick).AddListener(new UnityAction(ToggleReportDisplay)); CanvasUpdateButtonVisibility canvasUpdateButtonVisibility = val2.AddComponent<CanvasUpdateButtonVisibility>(); canvasUpdateButtonVisibility.button = ((Component)component).gameObject; canvasUpdateButtonVisibility.mainMenuTarget = gameObject; contentGameObject = ((Component)val2.transform.Find("Scroll View")).gameObject; contentParent = val2.transform.Find("Scroll View/Viewport/Content"); blockPrefab = ((Component)contentParent.Find("Block")).gameObject; spacingPrefab = ((Component)contentParent.Find("Divider")).gameObject; errorsTextMesh = ((Component)contentParent.Find("Errors")).GetComponent<TextMeshProUGUI>(); blockPrefab.SetActive(false); spacingPrefab.SetActive(false); loadedReport = false; } catch { } } private static void ToggleReportDisplay() { if (!contentGameObject.activeSelf) { if (Assets.BoomboxesLoading > 0) { ((TMP_Text)errorsTextMesh).text = $"{Assets.BoomboxesLoading} boomboxes still loading... Please refresh this display in a moment"; contentGameObject.SetActive(true); return; } if (!loadedReport) { SetupReportDisplay(); loadedReport = true; } contentGameObject.SetActive(true); } else { contentGameObject.SetActive(false); } } private static void SetupReportDisplay() { //IL_0101: Unknown result type (might be due to invalid IL or missing references) foreach (PersonalBoomboxPlugin.PBData datum in PersonalBoomboxPlugin.data) { GameObject val = Object.Instantiate<GameObject>(blockPrefab, contentParent); Transform transform = val.transform; ((TMP_Text)((Component)transform.Find("Title")).GetComponent<TextMeshProUGUI>()).text = "[" + datum.prefixName + "] " + datum.name; Transform val2 = transform.Find("Icon Area"); ((Component)val2.Find("Icon")).GetComponent<Image>().sprite = datum.icon; ((TMP_Text)((Component)val2.Find("Text 1")).GetComponent<TextMeshProUGUI>()).text = datum.feedback.GetSongsReport(); Transform val3 = val2.Find("Decal"); if ((Object)(object)datum.decal == (Object)null) { ((Component)val3).gameObject.SetActive(false); } else { RawImage component = ((Component)val3).GetComponent<RawImage>(); component.texture = (Texture)(object)datum.decal; ((Component)component).transform.localScale = datum.decalScale; } ((TMP_Text)((Component)transform.Find("Errors")).GetComponent<TextMeshProUGUI>()).text = datum.feedback.GetMainReport(); val.SetActive(true); GameObject val4 = Object.Instantiate<GameObject>(spacingPrefab, contentParent); val4.SetActive(true); } ((TMP_Text)errorsTextMesh).text = PersonalBoomboxPlugin.GetBonusReport(); ((TMP_Text)errorsTextMesh).transform.SetAsLastSibling(); } public static void SetContentDisplay(bool state) { if (!((Object)(object)contentGameObject == (Object)null)) { contentGameObject.SetActive(state); } } private static void PrintTransforms(Transform parent, int counter) { //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Expected O, but got Unknown string text = new string(' ', counter); Canvas component = ((Component)parent).GetComponent<Canvas>(); string text2 = (Object.op_Implicit((Object)(object)component) ? $" ({component.sortingOrder})" : string.Empty); PersonalBoomboxPlugin.Instance.logger.LogInfo((object)(text + ((Object)parent).name + text2)); foreach (Transform item in parent) { Transform parent2 = item; PrintTransforms(parent2, counter + 1); } } } public class CanvasUpdateButtonVisibility : MonoBehaviour { public GameObject mainMenuTarget; public GameObject button; public void LateUpdate() { if (!((Object)(object)mainMenuTarget == (Object)null)) { if (mainMenuTarget.activeInHierarchy) { button.SetActive(true); return; } button.SetActive(false); MenuManagerPatch.SetContentDisplay(state: false); } } } [HarmonyPatch(typeof(StartOfRound))] internal class StartOfRoundPatch { public static bool done; [HarmonyPatch("Start")] [HarmonyPrefix] public static void StartPatch(ref StartOfRound __instance) { if (done) { return; } done = true; PersonalBoomboxPlugin.Instance.logger.LogInfo((object)"Adding personal boomboxes to items list"); foreach (PersonalBoomboxPlugin.PBData datum in PersonalBoomboxPlugin.data) { bool isFinished = datum.IsFinished; bool isValid = datum.IsValid; if (datum.IsFinished && datum.IsValid) { __instance.allItemsList.itemsList.Add(datum.item); } } } } [HarmonyPatch(typeof(Terminal))] internal class TerminalPatch { public static Item boomboxItem; public static GameObject boomboxPrefab; public static MeshFilter boomboxMeshFilter; public static MeshRenderer boomboxMeshRenderer; public static AudioSource boomboxAudioSource; public static BoomboxItem boomboxScript; public static TerminalKeyword buyKeyword; public static TerminalKeyword infoKeyword; public static CompatibleNoun buyNoun; public static CompatibleNoun infoNoun; public static int copyTerminalNodeId; [HarmonyPatch("Awake")] [HarmonyPostfix] private static void AwakePatch(ref Terminal __instance) { foreach (PersonalBoomboxPlugin.PBData datum in PersonalBoomboxPlugin.data) { bool isFinished = datum.IsFinished; bool isValid = datum.IsValid; if (datum.IsFinished && datum.IsValid) { AddToTerminal(ref __instance, datum); continue; } if (!datum.IsFinished) { PersonalBoomboxPlugin.Instance.logger.LogError((object)(datum.boomboxName + " has no script initialized or it is not done loading music")); } if (!datum.IsValid) { PersonalBoomboxPlugin.Instance.logger.LogError((object)(datum.boomboxName + " has no script initialized or it has no loaded music")); } } Object.Instantiate<GameObject>(Assets.volumeControlCanvasPrefab); ((MonoBehaviour)MyOwnCoroutine.Instance).StartCoroutine(UpdateNetworkConfig()); } private static IEnumerator UpdateNetworkConfig() { while (!SyncedInstance<PluginConfig>.Synced) { yield return null; } int batvalue = SyncedInstance<PluginConfig>.Instance.batteryValue; bool pocketvalue = SyncedInstance<PluginConfig>.Instance.pocketCarryValue; foreach (PersonalBoomboxPlugin.PBData d in PersonalBoomboxPlugin.data) { d.item.batteryUsage = batvalue; d.prefab.GetComponent<PersonalBoomboxItem>().continuePlayingOnPocket = pocketvalue; } PersonalBoomboxPlugin.Instance.logger.LogInfo((object)"Set networked config values"); } private static void AddToTerminal(ref Terminal __instance, PersonalBoomboxPlugin.PBData data) { PersonalBoomboxPlugin.Instance.logger.LogInfo((object)("Adding " + data.boomboxName + " to terminal")); if ((Object)(object)data.item == (Object)null) { SetupPrefab(ref __instance, data); } List<Item> list = __instance.buyableItemsList.ToList(); data.itemIndex = list.Count; list.Add(data.item); __instance.buyableItemsList = list.ToArray(); if (!data.addedToTerminalKeyword) { AddToTerminalKeywords(ref __instance, data); data.addedToTerminalKeyword = true; } } private static void SetupPrefab(ref Terminal __instance, PersonalBoomboxPlugin.PBData data) { //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_01fe: Unknown result type (might be due to invalid IL or missing references) //IL_0208: Expected O, but got Unknown //IL_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_01c3: Unknown result type (might be due to invalid IL or missing references) //IL_01c8: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)boomboxItem == (Object)null) { InitializePrefab(ref __instance); } Item val = Object.Instantiate<Item>(boomboxItem); GameObject prefab = data.prefab; PersonalBoomboxItem script = data.script; val.creditsWorth = data.GetPrice; val.spawnPrefab = prefab; val.itemName = data.boomboxName; val.itemIcon = data.icon; data.item = val; prefab.tag = boomboxPrefab.tag; prefab.layer = boomboxPrefab.layer; MeshFilter component = prefab.GetComponent<MeshFilter>(); component.mesh = boomboxMeshFilter.mesh; MeshRenderer component2 = prefab.GetComponent<MeshRenderer>(); ((Renderer)component2).materials = ((Renderer)boomboxMeshRenderer).materials; ((Renderer)component2).materials[3].color = data.color; ((Renderer)component2).materials[1].color = DarkenColor(data.color); AudioSource component3 = prefab.GetComponent<AudioSource>(); component3.volume = data.volume; component3.outputAudioMixerGroup = boomboxAudioSource.outputAudioMixerGroup; component3.SetCustomCurve((AudioSourceCurveType)0, boomboxAudioSource.GetCustomCurve((AudioSourceCurveType)0)); component3.SetCustomCurve((AudioSourceCurveType)1, boomboxAudioSource.GetCustomCurve((AudioSourceCurveType)1)); component3.SetCustomCurve((AudioSourceCurveType)3, boomboxAudioSource.GetCustomCurve((AudioSourceCurveType)3)); component3.SetCustomCurve((AudioSourceCurveType)2, boomboxAudioSource.GetCustomCurve((AudioSourceCurveType)2)); if (Object.op_Implicit((Object)(object)data.decal)) { GameObject gameObject = ((Component)prefab.transform.GetChild(0)).gameObject; MeshRenderer component4 = gameObject.GetComponent<MeshRenderer>(); Material material = ((Renderer)component4).material; material.mainTexture = (Texture)(object)data.decal; ((Renderer)component4).material = material; gameObject.transform.localScale = Vector3.Scale(gameObject.transform.localScale, data.decalScale); gameObject.SetActive(true); } script.stopAudios = boomboxScript.stopAudios; ((GrabbableObject)script).insertedBattery = new Battery(false, ((GrabbableObject)boomboxScript).insertedBattery.charge); ((GrabbableObject)script).itemProperties = val; static Color DarkenColor(Color c) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0019: 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_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) return new Color(c.r * 0.9f, c.g * 0.9f, c.b * 0.9f, 1f); } } private static void InitializePrefab(ref Terminal __instance) { List<Item> list = __instance.buyableItemsList.ToList(); int num = -1; for (int i = 0; i < list.Count; i++) { Item val = list[i]; if (val.itemName.ToLowerInvariant() == "boombox") { num = i; break; } } if (num == -1) { PersonalBoomboxPlugin.Instance.logger.LogError((object)"Items has no boomerbox item to copy from"); return; } boomboxItem = list[num]; boomboxPrefab = boomboxItem.spawnPrefab; boomboxMeshFilter = boomboxPrefab.GetComponent<MeshFilter>(); boomboxMeshRenderer = boomboxPrefab.GetComponent<MeshRenderer>(); boomboxAudioSource = boomboxPrefab.GetComponent<AudioSource>(); boomboxScript = boomboxPrefab.GetComponent<BoomboxItem>(); } private static void AddToTerminalKeywords(ref Terminal __instance, PersonalBoomboxPlugin.PBData data) { //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Expected O, but got Unknown if ((Object)(object)buyKeyword == (Object)null) { InitializeKeyword(ref __instance); } string word = data.boomboxName.ToLowerInvariant(); string boomboxName = data.boomboxName; CompatibleNoun val = CopyNoun(buyNoun); TerminalKeyword noun = val.noun; noun.word = word; TerminalNode result = val.result; result.buyItemIndex = data.itemIndex; result.displayText = result.displayText.Replace("boom boxes", boomboxName + " boom boxes"); result.terminalOptions = CopyNouns(result.terminalOptions); TerminalNode val2 = FindNodeWithConfirm(result.terminalOptions); val2.buyItemIndex = data.itemIndex; val2.displayText = val2.displayText.Replace("boom boxes", boomboxName + " boom boxes"); List<CompatibleNoun> list = buyKeyword.compatibleNouns.ToList(); List<CompatibleNoun> list2 = infoKeyword.compatibleNouns.ToList(); List<TerminalKeyword> list3 = __instance.terminalNodes.allKeywords.ToList(); list.Add(val); buyKeyword.compatibleNouns = list.ToArray(); CompatibleNoun val3 = new CompatibleNoun(); val3.noun = val.noun; val3.result = CopyTerminalNode(infoNoun.result); val3.result.displayText = "\n" + data.description + "\n\n"; list2.Add(val3); infoKeyword.compatibleNouns = list2.ToArray(); list3.Add(noun); __instance.terminalNodes.allKeywords = list3.ToArray(); } private static void InitializeKeyword(ref Terminal __instance) { TerminalKeyword[] allKeywords = __instance.terminalNodes.allKeywords; buyKeyword = ((IEnumerable<TerminalKeyword>)allKeywords).FirstOrDefault((Func<TerminalKeyword, bool>)((TerminalKeyword a) => a.word == "buy")); infoKeyword = ((IEnumerable<TerminalKeyword>)allKeywords).FirstOrDefault((Func<TerminalKeyword, bool>)((TerminalKeyword a) => a.word == "info")); buyNoun = buyKeyword.compatibleNouns.First((CompatibleNoun b) => b.noun.word == "boombox"); infoNoun = infoKeyword.compatibleNouns.First((CompatibleNoun b) => b.noun.word == "boombox"); } private static TerminalKeyword CopyTerminalKeyword(TerminalKeyword target) { TerminalKeyword val = Object.Instantiate<TerminalKeyword>(target); ((Object)val).name = $"{((Object)val).name} {++copyTerminalNodeId}"; return val; } private static TerminalNode CopyTerminalNode(TerminalNode target) { TerminalNode val = Object.Instantiate<TerminalNode>(target); ((Object)val).name = $"{((Object)val).name} {++copyTerminalNodeId}"; return val; } private static CompatibleNoun CopyNoun(CompatibleNoun target) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected O, but got Unknown CompatibleNoun val = new CompatibleNoun(); val.noun = CopyTerminalKeyword(target.noun); val.result = CopyTerminalNode(target.result); return val; } private static CompatibleNoun[] CopyNouns(CompatibleNoun[] target) { CompatibleNoun[] array = (CompatibleNoun[])(object)new CompatibleNoun[target.Length]; for (int i = 0; i < target.Length; i++) { array[i] = CopyNoun(target[i]); } return array; } private static TerminalNode FindNodeWithConfirm(CompatibleNoun[] nouns) { foreach (CompatibleNoun val in nouns) { if (val.noun.word.ToLowerInvariant() == "confirm") { return val.result; } } return null; } } }