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 PlaceableRadio v1.1.6
plugins/PlaceableRadio/PlaceableRadio.dll
Decompiled 4 hours agousing System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using Jotunn.Configs; using Jotunn.Entities; using Jotunn.Managers; using Jotunn.Utils; using UnityEngine; using UnityEngine.Networking; [assembly: AssemblyFileVersion("1.1.5")] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: AssemblyVersion("1.1.5.0")] namespace PlaceableRadio; [BepInPlugin("uk.co.jamespower.placeableradio", "Placeable Radio", "1.1.5")] [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] public sealed class RadioPlugin : BaseUnityPlugin { public const string ModGuid = "uk.co.jamespower.placeableradio"; public const string ModName = "Placeable Radio"; public const string ModVersion = "1.1.5"; public const string PrefabName = "piece_placeable_radio"; internal static ConfigEntry<string> TrackDirectory; internal static ConfigEntry<float> AudibleDistance; internal static ConfigEntry<float> Volume; internal static string PluginDirectory; internal static ManualLogSource ModLogger; private static Material RadioBaseMaterial; private void Awake() { //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Expected O, but got Unknown //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Expected O, but got Unknown ModLogger = ((BaseUnityPlugin)this).Logger; PluginDirectory = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location) ?? Paths.PluginPath; TrackDirectory = ((BaseUnityPlugin)this).Config.Bind<string>("Audio", "TrackDirectory", "Tracks", "Folder containing track01, track02 and subsequent MP3, OGG or WAV files. Relative paths are resolved from the mod folder."); AudibleDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Audio", "AudibleDistance", 25f, new ConfigDescription("Maximum distance at which the radio can be heard.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 100f), Array.Empty<object>())); Volume = ((BaseUnityPlugin)this).Config.Bind<float>("Audio", "Volume", 0.7f, new ConfigDescription("Local playback volume.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); PrefabManager.OnVanillaPrefabsAvailable += RegisterRadio; } private void RegisterRadio() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Expected O, but got Unknown //IL_00b2: Unknown result type (might be due to invalid IL or missing references) PieceConfig val = new PieceConfig(); val.Name = "Radio"; val.Description = "A radio that plays music for nearby Vikings."; val.PieceTable = PieceTables.Hammer; val.Category = PieceCategories.Furniture; val.CraftingStation = CraftingStations.Workbench; PieceConfig val2 = val; val2.AddRequirement("FineWood", 10, true); val2.AddRequirement("Bronze", 2, true); val2.AddRequirement("SurtlingCore", 1, true); CustomPiece val3 = new CustomPiece("piece_placeable_radio", true, val2); val3.Piece.m_notOnTiltingSurface = true; RadioBaseMaterial = FindValheimPieceMaterial(); BuildRadioModel(val3.PiecePrefab); val3.PiecePrefab.AddComponent<RadioBehaviour>(); val3.Piece.m_icon = RenderManager.Instance.Render(val3.PiecePrefab, RenderManager.IsometricRotation); PieceManager.Instance.AddPiece(val3); PrefabManager.OnVanillaPrefabsAvailable -= RegisterRadio; ((BaseUnityPlugin)this).Logger.LogInfo((object)"Registered the placeable radio."); } private static Material FindValheimPieceMaterial() { GameObject prefab = PrefabManager.Instance.GetPrefab("piece_chest_wood"); if ((Object)(object)prefab == (Object)null) { ModLogger.LogWarning((object)"Could not find the vanilla wooden chest material source."); return null; } Renderer[] componentsInChildren = prefab.GetComponentsInChildren<Renderer>(true); Renderer[] array = componentsInChildren; foreach (Renderer val in array) { if ((Object)(object)val.sharedMaterial != (Object)null && (Object)(object)val.sharedMaterial.shader != (Object)null) { return val.sharedMaterial; } } ModLogger.LogWarning((object)"The vanilla wooden chest has no usable material."); return null; } private static void BuildRadioModel(GameObject prefab) { //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Expected O, but got Unknown //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01bb: Unknown result type (might be due to invalid IL or missing references) //IL_01cf: Unknown result type (might be due to invalid IL or missing references) //IL_01fb: Unknown result type (might be due to invalid IL or missing references) //IL_020f: Unknown result type (might be due to invalid IL or missing references) //IL_0223: Unknown result type (might be due to invalid IL or missing references) //IL_0237: Unknown result type (might be due to invalid IL or missing references) //IL_0263: Unknown result type (might be due to invalid IL or missing references) //IL_0277: Unknown result type (might be due to invalid IL or missing references) //IL_028b: Unknown result type (might be due to invalid IL or missing references) //IL_02bc: Unknown result type (might be due to invalid IL or missing references) //IL_02d0: Unknown result type (might be due to invalid IL or missing references) //IL_02e4: Unknown result type (might be due to invalid IL or missing references) //IL_032c: Unknown result type (might be due to invalid IL or missing references) //IL_0340: Unknown result type (might be due to invalid IL or missing references) //IL_0354: Unknown result type (might be due to invalid IL or missing references) //IL_0368: Unknown result type (might be due to invalid IL or missing references) //IL_0394: Unknown result type (might be due to invalid IL or missing references) //IL_03a8: Unknown result type (might be due to invalid IL or missing references) //IL_03bc: Unknown result type (might be due to invalid IL or missing references) //IL_03d0: Unknown result type (might be due to invalid IL or missing references) //IL_03fc: Unknown result type (might be due to invalid IL or missing references) //IL_0410: Unknown result type (might be due to invalid IL or missing references) //IL_0424: Unknown result type (might be due to invalid IL or missing references) //IL_0455: Unknown result type (might be due to invalid IL or missing references) //IL_0469: Unknown result type (might be due to invalid IL or missing references) //IL_047d: Unknown result type (might be due to invalid IL or missing references) //IL_0491: Unknown result type (might be due to invalid IL or missing references) Renderer component = prefab.GetComponent<Renderer>(); if ((Object)(object)component != (Object)null) { Object.DestroyImmediate((Object)(object)component); } MeshFilter component2 = prefab.GetComponent<MeshFilter>(); if ((Object)(object)component2 != (Object)null) { Object.DestroyImmediate((Object)(object)component2); } Collider component3 = prefab.GetComponent<Collider>(); if ((Object)(object)component3 != (Object)null) { Object.DestroyImmediate((Object)(object)component3); } BoxCollider val = prefab.AddComponent<BoxCollider>(); val.center = new Vector3(0f, 0.55f, 0f); val.size = new Vector3(1.45f, 1.1f, 0.62f); GameObject val2 = new GameObject("Visual"); val2.transform.SetParent(prefab.transform, false); val2.transform.localPosition = new Vector3(0f, -0.05f, 0f); CreatePrimitive(val2.transform, "Body", (PrimitiveType)3, new Vector3(0f, 0.55f, 0f), new Vector3(1.4f, 1f, 0.55f), new Color(0.24f, 0.12f, 0.055f)); CreatePrimitive(val2.transform, "FrontPanel", (PrimitiveType)3, new Vector3(0f, 0.55f, -0.292f), new Vector3(1.25f, 0.84f, 0.025f), new Color(0.055f, 0.05f, 0.045f)); CreatePrimitive(val2.transform, "Speaker", (PrimitiveType)2, new Vector3(-0.35f, 0.54f, -0.325f), new Vector3(0.46f, 0.025f, 0.46f), new Color(0.13f, 0.12f, 0.1f), Quaternion.Euler(90f, 0f, 0f)); CreatePrimitive(val2.transform, "Dial", (PrimitiveType)2, new Vector3(0.4f, 0.68f, -0.335f), new Vector3(0.16f, 0.035f, 0.16f), new Color(0.7f, 0.54f, 0.2f), Quaternion.Euler(90f, 0f, 0f)); CreatePrimitive(val2.transform, "Tuner", (PrimitiveType)3, new Vector3(0.4f, 0.4f, -0.33f), new Vector3(0.35f, 0.12f, 0.03f), new Color(0.78f, 0.63f, 0.31f)); GameObject val3 = CreatePrimitive(val2.transform, "Indicator", (PrimitiveType)0, new Vector3(0.57f, 0.87f, -0.34f), new Vector3(0.08f, 0.08f, 0.04f), new Color(0.25f, 0.03f, 0.02f)); val3.GetComponent<Renderer>().material.EnableKeyword("_EMISSION"); CreatePrimitive(val2.transform, "HandleLeft", (PrimitiveType)3, new Vector3(-0.48f, 1.13f, 0f), new Vector3(0.09f, 0.35f, 0.09f), new Color(0.08f, 0.055f, 0.035f), Quaternion.Euler(0f, 0f, -20f)); CreatePrimitive(val2.transform, "HandleRight", (PrimitiveType)3, new Vector3(0.48f, 1.13f, 0f), new Vector3(0.09f, 0.35f, 0.09f), new Color(0.08f, 0.055f, 0.035f), Quaternion.Euler(0f, 0f, 20f)); CreatePrimitive(val2.transform, "HandleTop", (PrimitiveType)3, new Vector3(0f, 1.27f, 0f), new Vector3(0.92f, 0.09f, 0.09f), new Color(0.08f, 0.055f, 0.035f)); CreatePrimitive(val2.transform, "Antenna", (PrimitiveType)2, new Vector3(0.52f, 1.48f, 0.08f), new Vector3(0.035f, 0.56f, 0.035f), new Color(0.38f, 0.38f, 0.36f), Quaternion.Euler(0f, 0f, -12f)); } private static GameObject CreatePrimitive(Transform parent, string objectName, PrimitiveType type, Vector3 position, Vector3 scale, Color color, Quaternion? rotation = null) { //IL_0000: 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_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Expected O, but got Unknown //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) GameObject val = GameObject.CreatePrimitive(type); ((Object)val).name = objectName; val.transform.SetParent(parent, false); val.transform.localPosition = position; val.transform.localScale = scale; val.transform.localRotation = ((!rotation.HasValue) ? Quaternion.identity : rotation.Value); Collider component = val.GetComponent<Collider>(); if ((Object)(object)component != (Object)null) { Object.DestroyImmediate((Object)(object)component); } Renderer component2 = val.GetComponent<Renderer>(); if ((Object)(object)component2 != (Object)null && (Object)(object)RadioBaseMaterial != (Object)null) { Material val2 = new Material(RadioBaseMaterial); ((Object)val2).name = "PlaceableRadio_" + objectName; val2.color = color; Material sharedMaterial = val2; component2.sharedMaterial = sharedMaterial; } else if ((Object)(object)component2 != (Object)null && (Object)(object)component2.material != (Object)null) { component2.material.color = color; } return val; } } public sealed class RadioBehaviour : MonoBehaviour, Hoverable, Interactable { private const string RpcToggle = "PlaceableRadio_Toggle"; private const string RpcAdvance = "PlaceableRadio_Advance"; private static readonly int PoweredKey = StringExtensionMethods.GetStableHashCode("PlaceableRadio_Powered"); private static readonly int TrackIndexKey = StringExtensionMethods.GetStableHashCode("PlaceableRadio_TrackIndex"); private ZNetView _networkView; private AudioSource _audioSource; private Renderer _indicator; private readonly List<string> _trackPaths = new List<string>(); private readonly List<AudioClip> _tracks = new List<AudioClip>(); private bool _lastPowered; private bool _stateKnown; private bool _tracksLoaded; private bool _awaitingAdvance; private int _lastTrackIndex = -1; private float _nextStateCheck; private void Awake() { _networkView = ((Component)this).GetComponent<ZNetView>(); _audioSource = ((Component)this).gameObject.AddComponent<AudioSource>(); _audioSource.playOnAwake = false; _audioSource.loop = false; _audioSource.spatialBlend = 1f; _audioSource.rolloffMode = (AudioRolloffMode)1; _audioSource.minDistance = 2f; _audioSource.maxDistance = RadioPlugin.AudibleDistance.Value; _audioSource.volume = RadioPlugin.Volume.Value; _audioSource.dopplerLevel = 0f; Transform val = ((Component)this).transform.Find("Visual/Indicator"); if ((Object)(object)val != (Object)null) { _indicator = ((Component)val).GetComponent<Renderer>(); } if ((Object)(object)_networkView != (Object)null) { _networkView.Register("PlaceableRadio_Toggle", (Action<long>)ToggleRpc); _networkView.Register<int>("PlaceableRadio_Advance", (Action<long, int>)AdvanceRpc); } } private IEnumerator Start() { DiscoverTracks(); if (_trackPaths.Count == 0) { RadioPlugin.ModLogger.LogWarning((object)("No radio tracks found. Add track01.mp3, track02.mp3 and subsequent files to: " + GetTrackDirectory())); } else { if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsDedicated()) { yield break; } foreach (string trackPath in _trackPaths) { AudioType audioType = GetAudioType(trackPath); UnityWebRequest request = UnityWebRequestMultimedia.GetAudioClip(new Uri(trackPath).AbsoluteUri, audioType); try { yield return request.SendWebRequest(); if ((int)request.result != 1) { RadioPlugin.ModLogger.LogError((object)("Could not load radio track " + trackPath + ": " + request.error)); yield break; } AudioClip clip = DownloadHandlerAudioClip.GetContent(request); ((Object)clip).name = Path.GetFileNameWithoutExtension(trackPath); _tracks.Add(clip); } finally { ((IDisposable)request)?.Dispose(); } } _tracksLoaded = true; ApplyNetworkState(ReadPoweredState(), ReadTrackIndex()); } } private void Update() { if (!(Time.time < _nextStateCheck)) { _nextStateCheck = Time.time + 0.25f; _audioSource.maxDistance = RadioPlugin.AudibleDistance.Value; _audioSource.volume = RadioPlugin.Volume.Value; bool flag = ReadPoweredState(); int num = ReadTrackIndex(); if (!_stateKnown || flag != _lastPowered || num != _lastTrackIndex) { ApplyNetworkState(flag, num); } if (flag && _tracksLoaded && _tracks.Count > 1 && (Object)(object)_audioSource.clip != (Object)null && !_audioSource.isPlaying && !_awaitingAdvance) { _awaitingAdvance = true; _networkView.InvokeRPC("PlaceableRadio_Advance", new object[1] { _lastTrackIndex }); } } } public string GetHoverName() { return "Radio"; } public string GetHoverText() { string text = ((!ReadPoweredState()) ? "Turn on" : "Turn off"); return Localization.instance.Localize("Radio\n[<color=yellow><b>$KEY_Use</b></color>] " + text); } public bool Interact(Humanoid user, bool hold, bool alt) { if (hold || (Object)(object)_networkView == (Object)null || !_networkView.IsValid()) { return false; } _networkView.InvokeRPC("PlaceableRadio_Toggle", Array.Empty<object>()); return true; } public bool UseItem(Humanoid user, ItemData item) { return false; } private void ToggleRpc(long sender) { if (!((Object)(object)_networkView == (Object)null) && _networkView.IsValid() && _networkView.IsOwner()) { bool flag = !ReadPoweredState(); _networkView.GetZDO().Set(PoweredKey, flag); if (flag && ReadTrackIndex() >= _trackPaths.Count) { _networkView.GetZDO().Set(TrackIndexKey, 0, false); } ApplyNetworkState(flag, ReadTrackIndex()); } } private void AdvanceRpc(long sender, int completedTrackIndex) { if (!((Object)(object)_networkView == (Object)null) && _networkView.IsValid() && _networkView.IsOwner() && ReadPoweredState() && _trackPaths.Count >= 2) { int num = ReadTrackIndex(); if (num == completedTrackIndex) { int num2 = (num + 1) % _trackPaths.Count; _networkView.GetZDO().Set(TrackIndexKey, num2, false); ApplyNetworkState(powered: true, num2); } } } private bool ReadPoweredState() { return (Object)(object)_networkView != (Object)null && _networkView.IsValid() && _networkView.GetZDO().GetBool(PoweredKey, false); } private int ReadTrackIndex() { if ((Object)(object)_networkView == (Object)null || !_networkView.IsValid() || _trackPaths.Count == 0) { return 0; } int num = _networkView.GetZDO().GetInt(TrackIndexKey, 0); return (num >= 0 && num < _trackPaths.Count) ? num : 0; } private void ApplyNetworkState(bool powered, int trackIndex) { //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_004f: 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) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00af: 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) bool flag = trackIndex != _lastTrackIndex; _lastPowered = powered; _lastTrackIndex = trackIndex; _stateKnown = true; _awaitingAdvance = false; if ((Object)(object)_indicator != (Object)null) { Color val = ((!powered) ? new Color(0.25f, 0.03f, 0.02f) : new Color(1f, 0.18f, 0.04f)); _indicator.material.color = val; if (_indicator.material.HasProperty("_EmissionColor")) { _indicator.material.SetColor("_EmissionColor", (!powered) ? Color.black : (val * 1.5f)); } } if (_tracksLoaded && trackIndex >= 0 && trackIndex < _tracks.Count) { if (flag || (Object)(object)_audioSource.clip == (Object)null) { _audioSource.Stop(); _audioSource.clip = _tracks[trackIndex]; _audioSource.loop = _tracks.Count == 1; } if (powered && !_audioSource.isPlaying) { _audioSource.Play(); } else if (!powered && _audioSource.isPlaying) { _audioSource.Stop(); } } } private void DiscoverTracks() { _trackPaths.Clear(); string trackDirectory = GetTrackDirectory(); if (!Directory.Exists(trackDirectory)) { return; } string[] files = Directory.GetFiles(trackDirectory); foreach (string text in files) { if (TryGetTrackNumber(text, out var _)) { _trackPaths.Add(text); } } _trackPaths.Sort(delegate(string left, string right) { TryGetTrackNumber(left, out var trackNumber2); TryGetTrackNumber(right, out var trackNumber3); int num = trackNumber2.CompareTo(trackNumber3); return (num == 0) ? string.Compare(left, right, StringComparison.OrdinalIgnoreCase) : num; }); } private static string GetTrackDirectory() { string value = RadioPlugin.TrackDirectory.Value; return (!Path.IsPathRooted(value)) ? Path.Combine(RadioPlugin.PluginDirectory, value) : value; } private static bool TryGetTrackNumber(string path, out int trackNumber) { trackNumber = 0; string text = Path.GetExtension(path).ToLowerInvariant(); if (text != ".mp3" && text != ".ogg" && text != ".wav") { return false; } string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(path); if (!fileNameWithoutExtension.StartsWith("track", StringComparison.OrdinalIgnoreCase) || fileNameWithoutExtension.Length <= 5) { return false; } return int.TryParse(fileNameWithoutExtension.Substring(5), out trackNumber) && trackNumber > 0; } private static AudioType GetAudioType(string path) { return (AudioType)(Path.GetExtension(path).ToLowerInvariant() switch { ".mp3" => 13, ".ogg" => 14, ".wav" => 20, _ => 0, }); } }