Decompiled source of PlaceableRadio v1.1.6

plugins/PlaceableRadio/PlaceableRadio.dll

Decompiled 4 hours ago
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<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, 
		});
	}
}