using 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 TrackDirectory; internal static ConfigEntry AudibleDistance; internal static ConfigEntry 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("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("Audio", "AudibleDistance", 25f, new ConfigDescription("Maximum distance at which the radio can be heard.", (AcceptableValueBase)(object)new AcceptableValueRange(5f, 100f), Array.Empty())); Volume = ((BaseUnityPlugin)this).Config.Bind("Audio", "Volume", 0.7f, new ConfigDescription("Local playback volume.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); 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(); 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(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(); if ((Object)(object)component != (Object)null) { Object.DestroyImmediate((Object)(object)component); } MeshFilter component2 = prefab.GetComponent(); if ((Object)(object)component2 != (Object)null) { Object.DestroyImmediate((Object)(object)component2); } Collider component3 = prefab.GetComponent(); if ((Object)(object)component3 != (Object)null) { Object.DestroyImmediate((Object)(object)component3); } BoxCollider val = prefab.AddComponent(); 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().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(); if ((Object)(object)component != (Object)null) { Object.DestroyImmediate((Object)(object)component); } Renderer component2 = val.GetComponent(); 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 _trackPaths = new List(); private readonly List _tracks = new List(); 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(); _audioSource = ((Component)this).gameObject.AddComponent(); _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(); } if ((Object)(object)_networkView != (Object)null) { _networkView.Register("PlaceableRadio_Toggle", (Action)ToggleRpc); _networkView.Register("PlaceableRadio_Advance", (Action)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[$KEY_Use] " + 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()); 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, }); } }