Decompiled source of PerfectProximityVoice v0.2.5

PerfectProximityVoice.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("PerfectProximityVoice")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("PerfectProximityVoice")]
[assembly: AssemblyTitle("PerfectProximityVoice")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace PerfectProximityVoice
{
	[BepInPlugin("perfect.proximity.voice", "Perfect Proximity Voice", "0.2.5")]
	public sealed class Plugin : BaseUnityPlugin
	{
		private sealed class NativeSliderControl
		{
			private readonly Plugin owner;

			private readonly long uid;

			private readonly GameObject gameObject;

			private readonly RectTransform rect;

			private readonly Component slider;

			private readonly PropertyInfo valueProperty;

			private bool changing;

			private static Sprite goldHandleSprite;

			internal NativeSliderControl(Plugin plugin, long playerUid, GameObject clone, Type sliderType)
			{
				owner = plugin;
				uid = playerUid;
				gameObject = clone;
				ref RectTransform reference = ref rect;
				Transform transform = clone.transform;
				reference = (RectTransform)(object)((transform is RectTransform) ? transform : null);
				slider = clone.GetComponent(sliderType);
				valueProperty = sliderType.GetProperty("value");
				sliderType.GetProperty("minValue")?.SetValue(slider, 0f, null);
				sliderType.GetProperty("maxValue")?.SetValue(slider, 2f, null);
				sliderType.GetProperty("wholeNumbers")?.SetValue(slider, false, null);
				HideHandleArtwork(clone);
				Component[] componentsInChildren = clone.GetComponentsInChildren<Component>(true);
				foreach (Component val in componentsInChildren)
				{
					if ((Object)(object)val == (Object)null || (Object)(object)val == (Object)(object)slider)
					{
						continue;
					}
					switch (((object)val).GetType().Name)
					{
					case "Text":
					case "TextMeshProUGUI":
					case "TMP_Text":
					{
						PropertyInfo property = ((object)val).GetType().GetProperty("text");
						if (property != null && property.CanWrite)
						{
							property.SetValue(val, "", null);
						}
						break;
					}
					}
				}
				try
				{
					object obj = sliderType.GetProperty("onValueChanged")?.GetValue(slider, null);
					(obj?.GetType().GetMethod("RemoveAllListeners"))?.Invoke(obj, null);
					MethodInfo methodInfo = obj?.GetType().GetMethod("AddListener");
					if (methodInfo != null)
					{
						Type parameterType = methodInfo.GetParameters()[0].ParameterType;
						SliderRelay firstArgument = new SliderRelay(owner, uid);
						Delegate obj2 = Delegate.CreateDelegate(parameterType, firstArgument, typeof(SliderRelay).GetMethod("Changed"));
						methodInfo.Invoke(obj, new object[1] { obj2 });
					}
				}
				catch (Exception ex)
				{
					((BaseUnityPlugin)owner).Logger.LogWarning((object)("Could not bind native slider: " + ex.Message));
				}
				SetValue(owner.GetPlayerVolume(uid));
			}

			private static void HideHandleArtwork(GameObject clone)
			{
				Slider component = clone.GetComponent<Slider>();
				if (!((Object)(object)component == (Object)null) && !((Object)(object)component.handleRect == (Object)null))
				{
					Graphic[] componentsInChildren = ((Component)component.handleRect).GetComponentsInChildren<Graphic>(true);
					for (int i = 0; i < componentsInChildren.Length; i++)
					{
						((Behaviour)componentsInChildren[i]).enabled = false;
					}
				}
			}

			private static Sprite MakeGoldHandleSprite()
			{
				//IL_0019: Unknown result type (might be due to invalid IL or missing references)
				//IL_001f: Expected O, but got Unknown
				//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
				//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
				//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
				//IL_0091: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
				if ((Object)(object)goldHandleSprite != (Object)null)
				{
					return goldHandleSprite;
				}
				Texture2D val = new Texture2D(48, 48, (TextureFormat)4, false);
				((Object)val).name = "PPV_GoldSliderHandle";
				for (int i = 0; i < 48; i++)
				{
					for (int j = 0; j < 48; j++)
					{
						float num = (float)j + 0.5f - 24f;
						float num2 = (float)i + 0.5f - 24f;
						float num3 = Mathf.Sqrt(num * num + num2 * num2);
						float num4 = Mathf.Clamp01((22.56f - num3) * 1.5f);
						Color val2 = ((num3 > 19.2f) ? new Color(0.38f, 0.23f, 0.07f, num4) : new Color(1f, 0.78f, 0.27f, num4));
						val.SetPixel(j, i, val2);
					}
				}
				val.Apply();
				goldHandleSprite = Sprite.Create(val, new Rect(0f, 0f, 48f, 48f), new Vector2(0.5f, 0.5f), 100f);
				return goldHandleSprite;
			}

			internal void SetValue(float value)
			{
				if (!((Object)(object)slider == (Object)null) && !(valueProperty == null) && !changing)
				{
					changing = true;
					try
					{
						valueProperty.SetValue(slider, Mathf.Clamp(value, 0f, 2f), null);
					}
					catch
					{
					}
					changing = false;
				}
			}

			internal void SetRect(float x, float y, float width, float height)
			{
				//IL_001f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0039: Unknown result type (might be due to invalid IL or missing references)
				//IL_0053: Unknown result type (might be due to invalid IL or missing references)
				//IL_0090: Unknown result type (might be due to invalid IL or missing references)
				//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
				if (!((Object)(object)rect == (Object)null))
				{
					rect.anchorMin = new Vector2(0.5f, 0.5f);
					rect.anchorMax = new Vector2(0.5f, 0.5f);
					rect.pivot = new Vector2(0.5f, 0.5f);
					rect.anchoredPosition = new Vector2(x + width * 0.5f - (float)Screen.width * 0.5f, (float)Screen.height * 0.5f - (y + height * 0.5f));
					rect.sizeDelta = new Vector2(width, height);
				}
			}

			internal void SetVisible(bool visible)
			{
				if ((Object)(object)gameObject != (Object)null && gameObject.activeSelf != visible)
				{
					gameObject.SetActive(visible);
				}
			}
		}

		private sealed class SliderRelay
		{
			private readonly Plugin owner;

			private readonly long uid;

			internal SliderRelay(Plugin plugin, long playerUid)
			{
				owner = plugin;
				uid = playerUid;
			}

			public void Changed(float value)
			{
				owner.OnNativeSliderChanged(uid, value);
			}
		}

		private sealed class Speaker
		{
			internal readonly AudioSource source;

			private readonly AudioReverbFilter dungeonReverb;

			private readonly AudioLowPassFilter occlusionFilter;

			internal float occlusionGain = 1f;

			internal float lastLocated;

			private readonly float[] ring;

			private float lastReverbLevel = float.NaN;

			private float lastReverbDecay = float.NaN;

			private float lastOcclusionGain = float.NaN;

			private float lastCutoff = float.NaN;

			private int write;

			private int read;

			private readonly object gate = new object();

			internal Speaker(long id, int rate, float distance)
			{
				//IL_0059: Unknown result type (might be due to invalid IL or missing references)
				//IL_005f: Expected O, but got Unknown
				//IL_0256: Unknown result type (might be due to invalid IL or missing references)
				//IL_0260: Expected O, but got Unknown
				GameObject val = new GameObject("PerfectProximityVoice_" + id);
				Object.DontDestroyOnLoad((Object)(object)val);
				source = val.AddComponent<AudioSource>();
				source.spatialBlend = 1f;
				source.reverbZoneMix = 0f;
				source.bypassEffects = false;
				source.bypassListenerEffects = true;
				source.bypassReverbZones = true;
				dungeonReverb = val.AddComponent<AudioReverbFilter>();
				((Behaviour)dungeonReverb).enabled = false;
				occlusionFilter = val.AddComponent<AudioLowPassFilter>();
				((Behaviour)occlusionFilter).enabled = false;
				occlusionFilter.cutoffFrequency = 22000f;
				occlusionFilter.lowpassResonanceQ = 1f;
				dungeonReverb.reverbPreset = (AudioReverbPreset)27;
				dungeonReverb.dryLevel = 0f;
				dungeonReverb.room = -650f;
				dungeonReverb.roomHF = -350f;
				dungeonReverb.roomLF = -800f;
				dungeonReverb.decayTime = 1.45f;
				dungeonReverb.decayHFRatio = 0.72f;
				dungeonReverb.reflectionsLevel = -850f;
				dungeonReverb.reflectionsDelay = 0.035f;
				dungeonReverb.reverbLevel = -350f;
				dungeonReverb.reverbDelay = 0.045f;
				dungeonReverb.diffusion = 75f;
				dungeonReverb.density = 70f;
				dungeonReverb.hfReference = 5000f;
				dungeonReverb.lfReference = 250f;
				source.rolloffMode = (AudioRolloffMode)1;
				source.minDistance = 5f;
				source.maxDistance = distance;
				source.loop = true;
				ring = new float[rate * 3];
				source.clip = AudioClip.Create("Voice_" + id, rate, 1, rate, true, new PCMReaderCallback(Fill));
				source.Play();
			}

			internal void SetOcclusion(float gain, float cutoff)
			{
				gain = Mathf.Clamp(gain, 0.1f, 1f);
				cutoff = Mathf.Clamp(cutoff, 500f, 22000f);
				if (Mathf.Abs(lastOcclusionGain - gain) > 0.005f)
				{
					occlusionGain = gain;
					lastOcclusionGain = gain;
				}
				if (!((Object)(object)occlusionFilter == (Object)null))
				{
					if (float.IsNaN(lastCutoff) || Mathf.Abs(lastCutoff - cutoff) > 5f)
					{
						occlusionFilter.cutoffFrequency = cutoff;
						lastCutoff = cutoff;
					}
					bool flag = gain < 0.999f || cutoff < 21999f;
					if (((Behaviour)occlusionFilter).enabled != flag)
					{
						((Behaviour)occlusionFilter).enabled = flag;
					}
				}
			}

			internal void SetDungeonReverb(bool enabled, float level, float decay)
			{
				if (!((Object)(object)dungeonReverb == (Object)null))
				{
					level = Mathf.Clamp(level, -10000f, 2000f);
					decay = Mathf.Clamp(decay, 0.1f, 3f);
					if (float.IsNaN(lastReverbLevel) || Mathf.Abs(lastReverbLevel - level) > 1f)
					{
						dungeonReverb.reverbLevel = level;
						lastReverbLevel = level;
					}
					if (float.IsNaN(lastReverbDecay) || Mathf.Abs(lastReverbDecay - decay) > 0.01f)
					{
						dungeonReverb.decayTime = decay;
						lastReverbDecay = decay;
					}
					if (((Behaviour)dungeonReverb).enabled != enabled)
					{
						((Behaviour)dungeonReverb).enabled = enabled;
					}
				}
			}

			internal void Push(byte[] data, float gain)
			{
				lock (gate)
				{
					for (int i = 0; i < data.Length; i++)
					{
						float num = MuLawDecode(data[i]) * gain;
						ring[write] = num / (1f + Mathf.Abs(num));
						write = (write + 1) % ring.Length;
						if (write == read)
						{
							read = (read + 1) % ring.Length;
						}
					}
				}
			}

			private void Fill(float[] output)
			{
				lock (gate)
				{
					for (int i = 0; i < output.Length; i++)
					{
						if (read == write)
						{
							output[i] = 0f;
							continue;
						}
						output[i] = ring[read];
						read = (read + 1) % ring.Length;
					}
				}
			}
		}

		private const string RpcName = "PPV_Audio_018";

		private readonly string sessionToken = Guid.NewGuid().ToString("N");

		private const int Rate = 16000;

		private const int Frame = 1600;

		private ConfigEntry<KeyCode> pushKey;

		private ConfigEntry<KeyCode> muteAllPlayersKey;

		private bool muteAllPlayers;

		private ConfigEntry<float> range;

		private ConfigEntry<float> volume;

		private ConfigEntry<float> voiceGain;

		private ConfigEntry<bool> caveReverbEnabled;

		private ConfigEntry<float> caveReverbLevel;

		private ConfigEntry<float> caveReverbDecay;

		private ConfigEntry<bool> obstructionEnabled;

		private ConfigEntry<float> wallVoiceVolume;

		private ConfigEntry<float> doorVoiceVolume;

		private ConfigEntry<float> wallLowPass;

		private ConfigEntry<float> doorLowPass;

		private float nextOcclusionCheck;

		private readonly RaycastHit[] occlusionHits = (RaycastHit[])(object)new RaycastHit[12];

		private float nextMicDeviceCheck;

		private bool hasMicrophoneDevice;

		private ConfigEntry<string> playerVolumes;

		private readonly Dictionary<long, float> individualVolumes = new Dictionary<long, float>();

		private GUIStyle voiceTitleStyle;

		private Vector2 voiceScroll;

		private bool voicePanelVisible;

		private float nextPlayerScan;

		private readonly Dictionary<long, string> knownNames = new Dictionary<long, string>();

		private ConfigEntry<string> microphoneDevice;

		private ConfigEntry<int> microphoneChannel;

		private string activeDevice;

		private float nextMicLog;

		private AudioClip microphone;

		private string micName;

		private int lastMicPosition;

		private float[] captureInterleaved = new float[3200];

		private float[] captureSamples = new float[1600];

		private byte[] captureEncoded = new byte[1600];

		private int captureBufferChannels = 2;

		private static readonly Vector3[] screenRectCorners = (Vector3[])(object)new Vector3[4];

		private readonly Dictionary<long, Speaker> speakers = new Dictionary<long, Speaker>();

		private object registeredOn;

		private MethodInfo invokeRpc;

		private bool warned;

		private bool transmitting;

		private GUIStyle playerLabelStyle;

		private GUIStyle playerValueStyle;

		private GUIStyle playerVoiceStyle;

		private Texture2D micIconTexture;

		private Font averiaFont;

		private Object averiaTmpFont;

		private object nativeSliderType;

		private Canvas playerControlsCanvas;

		private readonly Dictionary<long, NativeSliderControl> nativeSliders = new Dictionary<long, NativeSliderControl>();

		private float nextRegistration;

		private bool pickupRebound;

		private float nextPickupBind;

		private float nextDungeonCheck;

		private float nextFontRetry;

		private bool dungeonReverbActive;

		private FieldInfo insideDungeonField;

		private PropertyInfo insideDungeonProperty;

		private void Awake()
		{
			pushKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Voice", "PushToTalk", (KeyCode)118, "Hold to transmit.");
			muteAllPlayersKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Voice", "MuteAllPlayersKey", (KeyCode)0, "Toggle mute for all incoming player voices. Unassigned by default.");
			range = ((BaseUnityPlugin)this).Config.Bind<float>("Voice", "RangeMeters", 25f, "Maximum audible distance.");
			volume = ((BaseUnityPlugin)this).Config.Bind<float>("Voice", "Volume", 1f, "Incoming voice volume (0 to 1).");
			voiceGain = ((BaseUnityPlugin)this).Config.Bind<float>("Voice", "VoiceGain", 8f, "Incoming voice amplification. Soft limiter enabled.");
			caveReverbEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Audio", "DungeonReverb", true, "Enable subtle voice echo in dungeon environments.");
			caveReverbLevel = ((BaseUnityPlugin)this).Config.Bind<float>("Audio", "DungeonReverbLevel", -350f, "Medium wet reverb level in mB (-10000 to 2000). Lower is quieter.");
			caveReverbDecay = ((BaseUnityPlugin)this).Config.Bind<float>("Audio", "DungeonReverbDecay", 1.45f, "Medium dungeon voice reverb decay in seconds (0.1 to 3).");
			obstructionEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Audio", "ObstructionEnabled", true, "Muffle incoming voice when walls or doors block the listener's line of sight.");
			wallVoiceVolume = ((BaseUnityPlugin)this).Config.Bind<float>("Audio", "WallVoiceVolume", 0.58f, "Voice volume multiplier through one wall (0.1 to 1).");
			doorVoiceVolume = ((BaseUnityPlugin)this).Config.Bind<float>("Audio", "DoorVoiceVolume", 0.42f, "Voice volume multiplier through a door (0.1 to 1).");
			wallLowPass = ((BaseUnityPlugin)this).Config.Bind<float>("Audio", "WallLowPassHz", 2200f, "Low-pass cutoff through walls (500 to 8000 Hz).");
			doorLowPass = ((BaseUnityPlugin)this).Config.Bind<float>("Audio", "DoorLowPassHz", 1500f, "Low-pass cutoff through doors (500 to 8000 Hz).");
			playerVolumes = ((BaseUnityPlugin)this).Config.Bind<string>("Voice", "IndividualVolumes", "", "Per-player voice volumes as uid=percent entries separated by semicolons (0-200).");
			LoadPlayerVolumes();
			microphoneDevice = ((BaseUnityPlugin)this).Config.Bind<string>("Voice", "MicrophoneDevice", "", "Exact microphone name from BepInEx log. Empty = Windows/Unity default microphone.");
			microphoneChannel = ((BaseUnityPlugin)this).Config.Bind<int>("Voice", "MicrophoneChannel", 1, "1-based input channel. Focusrite Input 2 = 2. 0 = mix all channels.");
			microphoneDevice.SettingChanged += delegate
			{
				StopMicrophone();
			};
			LogMicrophones();
			LoadGameUiAssets();
			LoadPushToTalkIcon();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Perfect Proximity Voice 0.2.5 initialized (reused audio/UI buffers + global mute + per-player sliders; dungeon reverb unchanged)");
		}

		private void Update()
		{
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			if (Time.unscaledTime > nextRegistration)
			{
				nextRegistration = Time.unscaledTime + 2f;
				TryRegister();
			}
			if (!pickupRebound && Time.unscaledTime >= nextPickupBind)
			{
				nextPickupBind = Time.unscaledTime + 3f;
				TryRebindAutoPickup();
			}
			if (Time.unscaledTime >= nextMicDeviceCheck)
			{
				nextMicDeviceCheck = Time.unscaledTime + 5f;
				hasMicrophoneDevice = Microphone.devices.Length != 0;
			}
			if ((int)muteAllPlayersKey.Value != 0 && Input.GetKeyDown(muteAllPlayersKey.Value))
			{
				muteAllPlayers = !muteAllPlayers;
				((BaseUnityPlugin)this).Logger.LogInfo((object)("All incoming player voices " + (muteAllPlayers ? "muted." : "unmuted.")));
			}
			transmitting = Input.GetKey(pushKey.Value) && NetworkReady() && (Object)(object)Player.m_localPlayer != (Object)null && hasMicrophoneDevice;
			if (transmitting)
			{
				CaptureAndSend();
			}
			else
			{
				StopMicrophone();
			}
			if (Time.unscaledTime >= nextDungeonCheck)
			{
				nextDungeonCheck = Time.unscaledTime + 1f;
				dungeonReverbActive = IsLocalPlayerInDungeonOrCave();
			}
			bool flag = Time.unscaledTime >= nextOcclusionCheck;
			if (flag)
			{
				nextOcclusionCheck = Time.unscaledTime + 0.25f;
			}
			Player localPlayer = Player.m_localPlayer;
			float unscaledTime = Time.unscaledTime;
			foreach (KeyValuePair<long, Speaker> speaker in speakers)
			{
				Speaker value = speaker.Value;
				bool flag2 = unscaledTime - value.lastLocated < 0.55f;
				float num = ((unscaledTime - value.lastLocated < 1.5f && !muteAllPlayers) ? (Mathf.Clamp01(volume.Value) * GetPlayerVolume(speaker.Key)) : 0f);
				if (flag && flag2 && (Object)(object)localPlayer != (Object)null && obstructionEnabled.Value)
				{
					ApplyVoiceOcclusion(value, localPlayer);
				}
				else if (!obstructionEnabled.Value || !flag2)
				{
					value.SetOcclusion(1f, 22000f);
				}
				value.source.volume = num * value.occlusionGain;
				value.source.maxDistance = Mathf.Max(1f, range.Value);
				value.SetDungeonReverb(dungeonReverbActive && caveReverbEnabled.Value, caveReverbLevel.Value, caveReverbDecay.Value);
			}
		}

		private void ApplyVoiceOcclusion(Speaker speaker, Player listener)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: 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)
			//IL_0070: 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)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Vector3 val = ((Component)listener).transform.position + Vector3.up * 1f;
				Vector3 val2 = ((Component)speaker.source).transform.position + Vector3.up * 1f - val;
				float magnitude = ((Vector3)(ref val2)).magnitude;
				if (magnitude < 0.2f)
				{
					speaker.SetOcclusion(1f, 22000f);
					return;
				}
				int num = Physics.RaycastNonAlloc(val, val2 / magnitude, occlusionHits, Mathf.Max(0.1f, magnitude - 0.45f), -5, (QueryTriggerInteraction)1);
				int num2 = 0;
				bool flag = false;
				for (int i = 0; i < num; i++)
				{
					Collider collider = ((RaycastHit)(ref occlusionHits[i])).collider;
					if ((Object)(object)collider == (Object)null || !collider.enabled || collider.isTrigger)
					{
						continue;
					}
					Transform transform = ((Component)collider).transform;
					if ((Object)(object)transform == (Object)(object)((Component)listener).transform || transform.IsChildOf(((Component)listener).transform) || (Object)(object)((Component)collider).GetComponentInParent<Character>() != (Object)null)
					{
						continue;
					}
					string text = ((Object)transform).name;
					Transform parent = transform.parent;
					int num3 = 0;
					while (num3 < 3 && (Object)(object)parent != (Object)null)
					{
						text = text + " " + ((Object)parent).name;
						num3++;
						parent = parent.parent;
					}
					text = text.ToLowerInvariant();
					if (!text.Contains("player") && !text.Contains("character") && !text.Contains("vegetation") && !text.Contains("tree") && !text.Contains("bush") && !text.Contains("grass") && !text.Contains("piece_noforce") && !text.Contains("vfx") && !text.Contains("trigger"))
					{
						if (text.Contains("door") || text.Contains("gate") || text.Contains("hatch"))
						{
							flag = true;
						}
						else if (text.Contains("wall") || text.Contains("stone") || text.Contains("wood") || text.Contains("roof") || text.Contains("floor") || text.Contains("beam") || text.Contains("crypt") || text.Contains("piece"))
						{
							num2++;
						}
					}
				}
				if (flag)
				{
					speaker.SetOcclusion(Mathf.Clamp(doorVoiceVolume.Value, 0.1f, 1f), Mathf.Clamp(doorLowPass.Value, 500f, 8000f));
				}
				else if (num2 > 1)
				{
					speaker.SetOcclusion(Mathf.Clamp(wallVoiceVolume.Value * 0.72f, 0.1f, 1f), Mathf.Clamp(wallLowPass.Value * 0.72f, 500f, 8000f));
				}
				else if (num2 == 1)
				{
					speaker.SetOcclusion(Mathf.Clamp(wallVoiceVolume.Value, 0.1f, 1f), Mathf.Clamp(wallLowPass.Value, 500f, 8000f));
				}
				else
				{
					speaker.SetOcclusion(1f, 22000f);
				}
			}
			catch
			{
				speaker.SetOcclusion(1f, 22000f);
			}
		}

		private bool IsLocalPlayerInDungeonOrCave()
		{
			//IL_023c: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return false;
			}
			try
			{
				Type type = ((object)localPlayer).GetType();
				if (insideDungeonField == null && insideDungeonProperty == null)
				{
					string[] array = new string[4] { "m_insideDungeon", "m_inDungeon", "m_insideCave", "m_inCave" };
					foreach (string name in array)
					{
						insideDungeonField = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
						if (insideDungeonField != null && insideDungeonField.FieldType == typeof(bool))
						{
							break;
						}
						insideDungeonField = null;
						insideDungeonProperty = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
						if (insideDungeonProperty != null && insideDungeonProperty.PropertyType == typeof(bool))
						{
							break;
						}
						insideDungeonProperty = null;
					}
				}
				if (insideDungeonField != null && (bool)insideDungeonField.GetValue(localPlayer))
				{
					return true;
				}
				if (insideDungeonProperty != null && (bool)insideDungeonProperty.GetValue(localPlayer, null))
				{
					return true;
				}
				EnvMan instance = EnvMan.instance;
				if ((Object)(object)instance != (Object)null)
				{
					Type type2 = ((object)instance).GetType();
					string[] array = new string[2] { "GetCurrentEnvironment", "GetCurrentEnv" };
					foreach (string name2 in array)
					{
						MethodInfo method = type2.GetMethod(name2, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
						if (!(method == null) && IsDungeonEnvironment(method.Invoke(instance, null)))
						{
							return true;
						}
					}
					array = new string[3] { "m_forceEnvironment", "m_forcedEnvironment", "m_currentEnvironment" };
					foreach (string name3 in array)
					{
						FieldInfo field = type2.GetField(name3, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
						if (field != null && IsDungeonEnvironment(field.GetValue(instance)))
						{
							return true;
						}
					}
				}
				if (((Component)localPlayer).transform.position.y > 3000f)
				{
					return true;
				}
			}
			catch (Exception ex)
			{
				if (!warned)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)("Dungeon reverb detection failed: " + ex.Message));
					warned = true;
				}
			}
			return false;
		}

		private static bool IsDungeonEnvironment(object value)
		{
			if (value == null)
			{
				return false;
			}
			string text = value as string;
			if (text == null)
			{
				Type type = value.GetType();
				text = type.GetField("m_name", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(value) as string;
				if (text == null)
				{
					text = type.GetProperty("m_name")?.GetValue(value, null) as string;
				}
			}
			if (string.IsNullOrEmpty(text))
			{
				return false;
			}
			text = text.ToLowerInvariant();
			if (!text.Contains("crypt") && !text.Contains("dungeon") && !text.Contains("cave") && !text.Contains("burial") && !text.Contains("sunken") && !text.Contains("frost"))
			{
				return text.Contains("mine");
			}
			return true;
		}

		private void TryRebindAutoPickup()
		{
			try
			{
				Type typeFromHandle = typeof(ZInput);
				object obj = typeFromHandle.GetField("instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null);
				if (obj == null)
				{
					obj = typeFromHandle.GetProperty("instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null, null);
				}
				if (obj == null)
				{
					return;
				}
				string[] array = new string[3] { "AutoPickup", "AutoPickUp", "Auto pickup" };
				foreach (string text in array)
				{
					MethodInfo[] methods = typeFromHandle.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					foreach (MethodInfo methodInfo in methods)
					{
						if (methodInfo.Name != "ChangeButton" && methodInfo.Name != "SetButton")
						{
							continue;
						}
						ParameterInfo[] parameters = methodInfo.GetParameters();
						if (parameters.Length < 2 || parameters[0].ParameterType != typeof(string) || parameters[1].ParameterType != typeof(KeyCode))
						{
							continue;
						}
						object[] array2 = new object[parameters.Length];
						array2[0] = text;
						array2[1] = (object)(KeyCode)108;
						bool flag = true;
						for (int k = 2; k < parameters.Length; k++)
						{
							if (parameters[k].HasDefaultValue)
							{
								array2[k] = parameters[k].DefaultValue;
								continue;
							}
							if (parameters[k].ParameterType == typeof(bool))
							{
								array2[k] = false;
								continue;
							}
							flag = false;
							break;
						}
						if (flag)
						{
							methodInfo.Invoke(obj, array2);
							pickupRebound = true;
							((BaseUnityPlugin)this).Logger.LogInfo((object)("Vanilla Auto Pickup shortcut reassigned to L (binding: " + text + ")"));
							return;
						}
					}
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not rebind vanilla Auto Pickup to L: " + ex.Message));
				pickupRebound = true;
			}
		}

		private void OnGUI()
		{
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			DrawPlayerVoiceControls();
			if (transmitting && !((Object)(object)microphone == (Object)null) && !((Object)(object)micIconTexture == (Object)null))
			{
				float num = 66f;
				float num2 = 78f;
				float num3 = (float)Screen.width * 0.122f - 15f;
				float num4 = (float)Screen.height - 190f;
				GUI.color = Color.white;
				GUI.DrawTexture(new Rect(num3, num4, num, num2), (Texture)(object)micIconTexture, (ScaleMode)2, true);
			}
		}

		private void LoadPushToTalkIcon()
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Expected O, but got Unknown
			try
			{
				using Stream stream = typeof(Plugin).Assembly.GetManifestResourceStream("PerfectProximityVoice.Assets.voice_mic.png");
				if (stream == null)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)"Embedded push-to-talk microphone icon was not found.");
					return;
				}
				byte[] array = new byte[stream.Length];
				stream.Read(array, 0, array.Length);
				micIconTexture = new Texture2D(2, 2, (TextureFormat)4, false);
				((Object)micIconTexture).name = "PPV_PushToTalk_Mic_Stone";
				((Texture)micIconTexture).filterMode = (FilterMode)1;
				if (!ImageConversion.LoadImage(micIconTexture, array, true))
				{
					Object.Destroy((Object)(object)micIconTexture);
					micIconTexture = null;
					((BaseUnityPlugin)this).Logger.LogWarning((object)"Could not decode embedded push-to-talk microphone PNG.");
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not load push-to-talk microphone icon: " + ex.Message));
			}
		}

		private void LoadPlayerVolumes()
		{
			individualVolumes.Clear();
			string[] array = (playerVolumes.Value ?? "").Split(';');
			for (int i = 0; i < array.Length; i++)
			{
				string[] array2 = array[i].Split('=');
				if (array2.Length == 2 && long.TryParse(array2[0], out var result) && float.TryParse(array2[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var result2))
				{
					individualVolumes[result] = Mathf.Clamp(result2 / 100f, 0f, 2f);
				}
			}
		}

		private float GetPlayerVolume(long uid)
		{
			if (!individualVolumes.TryGetValue(uid, out var value))
			{
				return 1f;
			}
			return value;
		}

		private void SetPlayerVolume(long uid, float value)
		{
			individualVolumes[uid] = Mathf.Round(Mathf.Clamp(value, 0f, 2f) * 100f) / 100f;
			List<string> list = new List<string>();
			foreach (KeyValuePair<long, float> individualVolume in individualVolumes)
			{
				list.Add(individualVolume.Key + "=" + (individualVolume.Value * 100f).ToString("F0", CultureInfo.InvariantCulture));
			}
			playerVolumes.Value = string.Join(";", list.ToArray());
		}

		private bool IsVanillaPlayerListVisible()
		{
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Canvas[] array = Resources.FindObjectsOfTypeAll<Canvas>();
				foreach (Canvas val in array)
				{
					if (!Object.op_Implicit((Object)(object)val) || !((Component)val).gameObject.activeInHierarchy)
					{
						continue;
					}
					Component[] componentsInChildren = ((Component)val).GetComponentsInChildren<Component>(false);
					foreach (Component val2 in componentsInChildren)
					{
						if (!Object.op_Implicit((Object)(object)val2) || !val2.gameObject.activeInHierarchy)
						{
							continue;
						}
						Type type = ((object)val2).GetType();
						if (type.Name != "Text" && type.Name != "TextMeshProUGUI" && type.Name != "TMP_Text")
						{
							continue;
						}
						string text = type.GetProperty("text", BindingFlags.Instance | BindingFlags.Public)?.GetValue(val2, null) as string;
						if (string.IsNullOrEmpty(text))
						{
							continue;
						}
						string text2 = text.Trim().ToLowerInvariant();
						if (text2 != "список игроков" && text2 != "player list" && text2 != "players" && text2 != "$menu_players")
						{
							continue;
						}
						Transform transform = val2.transform;
						RectTransform val3 = (RectTransform)(object)((transform is RectTransform) ? transform : null);
						if (Object.op_Implicit((Object)(object)val3))
						{
							Rect val4 = ScreenRect(val3, val);
							if (((Rect)(ref val4)).width > 0f && ((Rect)(ref val4)).yMax < (float)Screen.height * 0.19f && ((Rect)(ref val4)).center.x > (float)Screen.width * 0.25f && ((Rect)(ref val4)).center.x < (float)Screen.width * 0.75f)
							{
								return true;
							}
						}
					}
				}
			}
			catch
			{
			}
			return false;
		}

		private static Rect ScreenRect(RectTransform rt, Canvas canvas)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: 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_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: 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_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			rt.GetWorldCorners(screenRectCorners);
			Camera obj = (((int)canvas.renderMode == 0) ? null : canvas.worldCamera);
			Vector2 val = RectTransformUtility.WorldToScreenPoint(obj, screenRectCorners[0]);
			Vector2 val2 = RectTransformUtility.WorldToScreenPoint(obj, screenRectCorners[2]);
			return Rect.MinMaxRect(val.x, (float)Screen.height - val2.y, val2.x, (float)Screen.height - val.y);
		}

		private void RefreshPlayerNames()
		{
			if (Time.unscaledTime < nextPlayerScan || (Object)(object)ZNet.instance == (Object)null)
			{
				return;
			}
			nextPlayerScan = Time.unscaledTime + 3f;
			try
			{
				if (!(typeof(ZNet).GetMethod("GetPeers", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.Invoke(ZNet.instance, null) is IEnumerable enumerable))
				{
					return;
				}
				foreach (object item in enumerable)
				{
					if (item != null)
					{
						Type type = item.GetType();
						object obj = type.GetField("m_uid", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(item);
						object obj2 = type.GetField("m_playerName", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(item);
						if (obj != null && obj2 is string && !string.IsNullOrEmpty((string)obj2))
						{
							knownNames[Convert.ToInt64(obj)] = (string)obj2;
						}
					}
				}
			}
			catch
			{
			}
		}

		private void LoadGameUiAssets()
		{
			try
			{
				Font[] array = Resources.FindObjectsOfTypeAll<Font>();
				foreach (Font val in array)
				{
					if ((Object)(object)val != (Object)null && IsAveriaBoldName(((Object)val).name))
					{
						averiaFont = val;
						break;
					}
				}
				Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
				for (int i = 0; i < assemblies.Length; i++)
				{
					Type type = assemblies[i].GetType("TMPro.TMP_FontAsset", throwOnError: false);
					if (type == null)
					{
						continue;
					}
					Object[] array2 = Resources.FindObjectsOfTypeAll(type);
					foreach (Object val2 in array2)
					{
						if (!(val2 == (Object)null) && IsAveriaBoldName(val2.name))
						{
							averiaTmpFont = val2;
							object? obj = type.GetProperty("sourceFontFile", BindingFlags.Instance | BindingFlags.Public)?.GetValue(val2, null);
							Font val3 = (Font)((obj is Font) ? obj : null);
							if ((Object)(object)val3 != (Object)null)
							{
								averiaFont = val3;
							}
							break;
						}
					}
					if (averiaTmpFont != (Object)null)
					{
						break;
					}
				}
				if ((Object)(object)averiaFont == (Object)null && averiaTmpFont == (Object)null)
				{
					if (Time.unscaledTime < 60f)
					{
						((BaseUnityPlugin)this).Logger.LogInfo((object)"AveriaSansLibre-Bold not loaded yet; will retry after game UI initializes.");
					}
				}
				else
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)"Exact AveriaSansLibre-Bold font asset found.");
				}
				assemblies = AppDomain.CurrentDomain.GetAssemblies();
				for (int i = 0; i < assemblies.Length; i++)
				{
					Type type2 = assemblies[i].GetType("UnityEngine.UI.Slider", throwOnError: false);
					if (!(type2 == null))
					{
						nativeSliderType = type2;
						break;
					}
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not load Valheim UI assets: " + ex.Message));
			}
		}

		private static bool IsAveriaBoldName(string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return false;
			}
			string text = name.Replace(" ", "").Replace("_", "").Replace("-", "")
				.ToLowerInvariant();
			if (text.Contains("averiasanslibre"))
			{
				return text.Contains("bold");
			}
			return false;
		}

		private void EnsurePlayerControlsCanvas()
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Expected O, but got Unknown
			if ((Object)(object)playerControlsCanvas != (Object)null)
			{
				return;
			}
			GameObject val = new GameObject("PerfectProximityVoice_PlayerControls");
			Object.DontDestroyOnLoad((Object)(object)val);
			playerControlsCanvas = val.AddComponent<Canvas>();
			playerControlsCanvas.renderMode = (RenderMode)0;
			playerControlsCanvas.sortingOrder = 32000;
			val.AddComponent<CanvasGroup>();
			Type type = null;
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			for (int i = 0; i < assemblies.Length; i++)
			{
				type = assemblies[i].GetType("UnityEngine.UI.GraphicRaycaster", throwOnError: false);
				if (type != null)
				{
					break;
				}
			}
			if (type != null)
			{
				val.AddComponent(type);
			}
		}

		private void DrawPlayerVoiceControls()
		{
			//IL_0196: Unknown result type (might be due to invalid IL or missing references)
			//IL_01df: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0216: Unknown result type (might be due to invalid IL or missing references)
			//IL_024f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0276: Unknown result type (might be due to invalid IL or missing references)
			if (!NetworkReady() || !IsVanillaPlayerListVisible())
			{
				HideNativeSliders();
				return;
			}
			RefreshPlayerNames();
			if ((Object)(object)averiaFont == (Object)null && Time.unscaledTime >= nextFontRetry)
			{
				nextFontRetry = Time.unscaledTime + 2f;
				LoadGameUiAssets();
			}
			ApplyAveriaToPlayerNames();
			EnsurePlayerLabelStyles();
			EnsurePlayerControlsCanvas();
			HashSet<long> hashSet = new HashSet<long>();
			foreach (KeyValuePair<long, string> knownName in knownNames)
			{
				if (knownName.Key == LocalUid() || !TryFindPlayerRow(knownName.Value, out var result) || ((Rect)(ref result)).width < 620f || ((Rect)(ref result)).height < 95f)
				{
					continue;
				}
				hashSet.Add(knownName.Key);
				float num = Mathf.Min(290f, ((Rect)(ref result)).width * 0.31f);
				float num2 = ((Rect)(ref result)).x + ((Rect)(ref result)).width * 0.51f - num * 0.5f;
				float num3 = ((Rect)(ref result)).yMax - 35f;
				float num4 = num2 + 78f;
				float num5 = num - 138f;
				float playerVolume = GetPlayerVolume(knownName.Key);
				if (nativeSliderType != null && TryCreateNativeSlider(knownName.Key, out var control))
				{
					control.SetVisible(visible: true);
					control.SetValue(playerVolume);
					control.SetRect(num4, num3 + 3f, num5, 24f);
				}
				else
				{
					float num6 = GUI.HorizontalSlider(new Rect(num4, num3 + 7f, num5, 19f), playerVolume, 0f, 2f);
					if (Mathf.Abs(num6 - playerVolume) > 0.01f)
					{
						SetPlayerVolume(knownName.Key, Mathf.Round(num6 * 20f) / 20f);
					}
				}
				Color color = GUI.color;
				GUI.color = new Color(0.98f, 0.86f, 0.55f, 1f);
				GUI.Label(new Rect(num2, num3 - 2f, 82f, 23f), "Голос", (playerValueStyle == null) ? playerVoiceStyle : GetVoiceCaptionStyle());
				GUI.color = new Color(1f, 0.88f, 0.52f, 1f);
				GUI.Label(new Rect(num2 + num - 51f, num3 - 2f, 55f, 23f), Mathf.RoundToInt(playerVolume * 100f) + "%", playerValueStyle);
				GUI.color = color;
			}
			foreach (KeyValuePair<long, NativeSliderControl> nativeSlider in nativeSliders)
			{
				nativeSlider.Value.SetVisible(hashSet.Contains(nativeSlider.Key));
			}
		}

		private void ApplyAveriaToPlayerNames()
		{
			//IL_01c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cd: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)averiaFont == (Object)null && averiaTmpFont == (Object)null)
			{
				return;
			}
			HashSet<string> hashSet = new HashSet<string>(knownNames.Values, StringComparer.OrdinalIgnoreCase);
			string localPlayerName = GetLocalPlayerName();
			if (!string.IsNullOrEmpty(localPlayerName))
			{
				hashSet.Add(localPlayerName);
			}
			if (hashSet.Count == 0)
			{
				return;
			}
			try
			{
				Canvas[] array = Resources.FindObjectsOfTypeAll<Canvas>();
				foreach (Canvas val in array)
				{
					if (!Object.op_Implicit((Object)(object)val) || !((Component)val).gameObject.activeInHierarchy)
					{
						continue;
					}
					Component[] componentsInChildren = ((Component)val).GetComponentsInChildren<Component>(false);
					foreach (Component val2 in componentsInChildren)
					{
						if (!Object.op_Implicit((Object)(object)val2) || !val2.gameObject.activeInHierarchy)
						{
							continue;
						}
						Type type = ((object)val2).GetType();
						if (type.Name != "Text" && type.Name != "TextMeshProUGUI" && type.Name != "TMP_Text")
						{
							continue;
						}
						string text = type.GetProperty("text", BindingFlags.Instance | BindingFlags.Public)?.GetValue(val2, null) as string;
						if (string.IsNullOrEmpty(text))
						{
							continue;
						}
						bool flag = text.IndexOf("(Host)", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("(Хост)", StringComparison.OrdinalIgnoreCase) >= 0;
						foreach (string item in hashSet)
						{
							if (!string.IsNullOrEmpty(item) && text.IndexOf(item, StringComparison.OrdinalIgnoreCase) >= 0)
							{
								flag = true;
								break;
							}
						}
						if (!flag)
						{
							continue;
						}
						Transform val3 = val2.transform;
						int num = 0;
						while (num < 5 && (Object)(object)val3 != (Object)null)
						{
							RectTransform val4 = (RectTransform)(object)((val3 is RectTransform) ? val3 : null);
							if (Object.op_Implicit((Object)(object)val4))
							{
								Rect val5 = ScreenRect(val4, val);
								if (!(((Rect)(ref val5)).width < 620f) && !(((Rect)(ref val5)).height < 95f) && !(((Rect)(ref val5)).height > 180f) && !(((Rect)(ref val5)).y < 0f) && !(((Rect)(ref val5)).yMax > (float)Screen.height))
								{
									ApplyAveriaFont(val2);
									break;
								}
							}
							num++;
							val3 = val3.parent;
						}
					}
				}
			}
			catch (Exception ex)
			{
				if (Time.frameCount % 600 == 0)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not apply Averia to player names: " + ex.Message));
				}
			}
		}

		private string GetLocalPlayerName()
		{
			try
			{
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer == (Object)null)
				{
					return null;
				}
				Type type = ((object)localPlayer).GetType();
				MethodInfo method = type.GetMethod("GetPlayerName", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
				if (method != null)
				{
					return method.Invoke(localPlayer, null) as string;
				}
				return type.GetField("m_name", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(localPlayer) as string;
			}
			catch
			{
				return null;
			}
		}

		private void ApplyAveriaFont(Component textComponent)
		{
			if ((Object)(object)textComponent == (Object)null)
			{
				return;
			}
			try
			{
				PropertyInfo property = ((object)textComponent).GetType().GetProperty("font", BindingFlags.Instance | BindingFlags.Public);
				if (!(property == null) && property.CanWrite)
				{
					if ((Object)(object)averiaFont != (Object)null && property.PropertyType.IsInstanceOfType(averiaFont))
					{
						property.SetValue(textComponent, averiaFont, null);
					}
					else if (averiaTmpFont != (Object)null && property.PropertyType.IsInstanceOfType(averiaTmpFont))
					{
						property.SetValue(textComponent, averiaTmpFont, null);
					}
				}
			}
			catch
			{
			}
		}

		private GUIStyle GetVoiceCaptionStyle()
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Expected O, but got Unknown
			if (playerVoiceStyle == null)
			{
				playerVoiceStyle = new GUIStyle(playerValueStyle);
			}
			playerVoiceStyle.font = playerValueStyle.font;
			playerVoiceStyle.fontSize = playerValueStyle.fontSize;
			playerVoiceStyle.fontStyle = playerValueStyle.fontStyle;
			playerVoiceStyle.alignment = (TextAnchor)3;
			playerVoiceStyle.normal.textColor = playerValueStyle.normal.textColor;
			return playerVoiceStyle;
		}

		private void EnsurePlayerLabelStyles()
		{
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Expected O, but got Unknown
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Expected O, but got Unknown
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Expected O, but got Unknown
			if (playerLabelStyle != null)
			{
				if ((Object)(object)averiaFont != (Object)null && (Object)(object)playerLabelStyle.font != (Object)(object)averiaFont)
				{
					playerLabelStyle.font = averiaFont;
					playerValueStyle.font = averiaFont;
					playerLabelStyle.fontStyle = (FontStyle)1;
					playerValueStyle.fontStyle = (FontStyle)1;
					if (playerVoiceStyle != null)
					{
						playerVoiceStyle.font = averiaFont;
						playerVoiceStyle.fontStyle = (FontStyle)1;
					}
				}
				return;
			}
			playerLabelStyle = new GUIStyle(GUI.skin.label);
			playerLabelStyle.fontSize = 16;
			playerLabelStyle.fontStyle = (FontStyle)1;
			playerLabelStyle.alignment = (TextAnchor)3;
			playerLabelStyle.normal.textColor = new Color(0.98f, 0.86f, 0.55f, 1f);
			playerValueStyle = new GUIStyle(playerLabelStyle);
			playerValueStyle.alignment = (TextAnchor)5;
			playerValueStyle.fontStyle = (FontStyle)1;
			playerValueStyle.normal.textColor = new Color(1f, 0.88f, 0.52f, 1f);
			playerVoiceStyle = new GUIStyle(playerValueStyle);
			playerVoiceStyle.alignment = (TextAnchor)3;
			playerVoiceStyle.fontStyle = (FontStyle)1;
			if ((Object)(object)averiaFont != (Object)null)
			{
				playerLabelStyle.font = averiaFont;
				playerValueStyle.font = averiaFont;
				playerVoiceStyle.font = averiaFont;
			}
		}

		private bool TryCreateNativeSlider(long uid, out NativeSliderControl control)
		{
			if (nativeSliders.TryGetValue(uid, out control))
			{
				return true;
			}
			try
			{
				Type type = nativeSliderType as Type;
				if (type == null)
				{
					return false;
				}
				Object[] array = Resources.FindObjectsOfTypeAll(type);
				Component val = null;
				Object[] array2 = array;
				foreach (Object obj in array2)
				{
					Component val2 = (Component)(object)((obj is Component) ? obj : null);
					if ((Object)(object)val2 == (Object)null || (Object)(object)val2.gameObject == (Object)null || ((Object)val2.gameObject).name.StartsWith("PPV_PlayerVolume_", StringComparison.Ordinal))
					{
						continue;
					}
					RectTransform component = val2.GetComponent<RectTransform>();
					Transform val3 = ((component != null) ? ((Transform)component).Find("Handle Slide Area/Handle") : null);
					if ((Object)(object)val3 != (Object)null)
					{
						Image component2 = ((Component)val3).GetComponent<Image>();
						if ((Object)(object)((component2 != null) ? component2.sprite : null) != (Object)null)
						{
							val = val2;
							break;
						}
					}
					if ((Object)(object)val == (Object)null && ((Object)val2.gameObject).name.IndexOf("slider", StringComparison.OrdinalIgnoreCase) >= 0)
					{
						val = val2;
					}
				}
				if ((Object)(object)val == (Object)null)
				{
					return false;
				}
				EnsurePlayerControlsCanvas();
				GameObject val4 = Object.Instantiate<GameObject>(val.gameObject, ((Component)playerControlsCanvas).transform, false);
				((Object)val4).name = "PPV_PlayerVolume_" + uid;
				val4.SetActive(true);
				control = new NativeSliderControl(this, uid, val4, type);
				nativeSliders[uid] = control;
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Created isolated volume slider for player UID " + uid));
				return true;
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not clone Valheim slider: " + ex.Message));
				return false;
			}
		}

		private void HideNativeSliders()
		{
			foreach (KeyValuePair<long, NativeSliderControl> nativeSlider in nativeSliders)
			{
				nativeSlider.Value.SetVisible(visible: false);
			}
		}

		private void OnNativeSliderChanged(long uid, float value)
		{
			SetPlayerVolume(uid, Mathf.Round(Mathf.Clamp(value, 0f, 2f) * 100f) / 100f);
		}

		private bool TryFindPlayerRow(string playerName, out Rect result)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0167: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			result = default(Rect);
			if (string.IsNullOrEmpty(playerName))
			{
				return false;
			}
			try
			{
				Canvas[] array = Resources.FindObjectsOfTypeAll<Canvas>();
				foreach (Canvas val in array)
				{
					if (!Object.op_Implicit((Object)(object)val) || !((Component)val).gameObject.activeInHierarchy)
					{
						continue;
					}
					Component[] componentsInChildren = ((Component)val).GetComponentsInChildren<Component>(false);
					foreach (Component val2 in componentsInChildren)
					{
						if (!Object.op_Implicit((Object)(object)val2) || !val2.gameObject.activeInHierarchy)
						{
							continue;
						}
						Type type = ((object)val2).GetType();
						if (type.Name != "Text" && type.Name != "TextMeshProUGUI" && type.Name != "TMP_Text")
						{
							continue;
						}
						string text = type.GetProperty("text", BindingFlags.Instance | BindingFlags.Public)?.GetValue(val2, null) as string;
						if (string.IsNullOrEmpty(text) || text.IndexOf(playerName, StringComparison.OrdinalIgnoreCase) < 0)
						{
							continue;
						}
						Transform val3 = val2.transform;
						int num = 0;
						while (num < 5 && (Object)(object)val3 != (Object)null)
						{
							RectTransform val4 = (RectTransform)(object)((val3 is RectTransform) ? val3 : null);
							if (Object.op_Implicit((Object)(object)val4))
							{
								Rect val5 = ScreenRect(val4, val);
								if (((Rect)(ref val5)).width >= 620f && ((Rect)(ref val5)).height >= 95f && ((Rect)(ref val5)).height <= 180f && ((Rect)(ref val5)).y >= 0f && ((Rect)(ref val5)).yMax <= (float)Screen.height)
								{
									result = val5;
									return true;
								}
							}
							num++;
							val3 = val3.parent;
						}
					}
				}
			}
			catch (Exception ex)
			{
				if (Time.frameCount % 600 == 0)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)("Player row UI: " + ex.Message));
				}
			}
			return false;
		}

		private bool NetworkReady()
		{
			if ((Object)(object)ZNet.instance != (Object)null)
			{
				return ZRoutedRpc.instance != null;
			}
			return false;
		}

		private void TryRegister()
		{
			object instance = ZRoutedRpc.instance;
			if (instance == null || instance == registeredOn)
			{
				return;
			}
			try
			{
				Type type = instance.GetType();
				MethodInfo methodInfo = null;
				MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public);
				foreach (MethodInfo methodInfo2 in methods)
				{
					if (methodInfo2.Name == "Register" && methodInfo2.IsGenericMethodDefinition && methodInfo2.GetGenericArguments().Length == 1 && methodInfo2.GetParameters().Length == 2)
					{
						methodInfo = methodInfo2;
						break;
					}
				}
				if (methodInfo == null)
				{
					throw new MissingMethodException("ZRoutedRpc.Register<T>");
				}
				Action<long, ZPackage> action = Receive;
				methodInfo.MakeGenericMethod(typeof(ZPackage)).Invoke(instance, new object[2] { "PPV_Audio_018", action });
				invokeRpc = null;
				methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public);
				foreach (MethodInfo methodInfo3 in methods)
				{
					if (methodInfo3.Name == "InvokeRoutedRPC" && methodInfo3.GetParameters().Length == 3 && methodInfo3.GetParameters()[1].ParameterType == typeof(string))
					{
						invokeRpc = methodInfo3;
						break;
					}
				}
				if (invokeRpc == null)
				{
					throw new MissingMethodException("ZRoutedRpc.InvokeRoutedRPC");
				}
				registeredOn = instance;
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Voice RPC registered");
			}
			catch (Exception ex)
			{
				if (!warned)
				{
					((BaseUnityPlugin)this).Logger.LogError((object)("Voice RPC registration failed: " + ex));
					warned = true;
				}
			}
		}

		private void LogMicrophones()
		{
			string[] devices = Microphone.devices;
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Available microphone devices (" + devices.Length + "):"));
			string[] array = devices;
			foreach (string text in array)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)("  Microphone: " + text));
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Configured microphone: " + (string.IsNullOrWhiteSpace(microphoneDevice.Value) ? "<system default>" : microphoneDevice.Value)));
		}

		private void StartMicrophone()
		{
			if ((Object)(object)microphone != (Object)null)
			{
				return;
			}
			string[] devices = Microphone.devices;
			if (devices.Length == 0)
			{
				return;
			}
			string text = (microphoneDevice.Value ?? "").Trim();
			string text2 = null;
			if (text.Length > 0)
			{
				bool flag = false;
				string[] array = devices;
				foreach (string text3 in array)
				{
					if (string.Equals(text3, text, StringComparison.OrdinalIgnoreCase))
					{
						text2 = text3;
						flag = true;
						break;
					}
				}
				if (!flag)
				{
					if (Time.unscaledTime >= nextMicLog)
					{
						((BaseUnityPlugin)this).Logger.LogWarning((object)("Configured microphone not found: " + text + ". Voice capture disabled until corrected."));
						LogMicrophones();
						nextMicLog = Time.unscaledTime + 15f;
					}
					return;
				}
			}
			try
			{
				micName = text2;
				microphone = Microphone.Start(micName, true, 2, 16000);
				lastMicPosition = 0;
				if ((Object)(object)microphone != (Object)null)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)("Microphone started: " + (micName ?? "<system default>")));
				}
				else
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)"Microphone.Start returned null");
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Microphone start failed: " + ex));
				microphone = null;
			}
		}

		private void StopMicrophone()
		{
			if (!((Object)(object)microphone == (Object)null))
			{
				try
				{
					Microphone.End(micName);
				}
				catch (Exception ex)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)("Microphone stop: " + ex.Message));
				}
				microphone = null;
				micName = null;
				lastMicPosition = 0;
			}
		}

		private long LocalUid()
		{
			if ((Object)(object)ZNet.instance == (Object)null)
			{
				return 0L;
			}
			try
			{
				MethodInfo method = typeof(ZNet).GetMethod("GetUID", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
				return (method == null) ? 0 : Convert.ToInt64(method.Invoke(ZNet.instance, null));
			}
			catch
			{
				return 0L;
			}
		}

		private bool IsOwnVoice(long sender)
		{
			if ((Object)(object)ZNet.instance == (Object)null)
			{
				return false;
			}
			try
			{
				MethodInfo method = typeof(ZNet).GetMethod("GetUID", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
				if (method != null)
				{
					return Convert.ToInt64(method.Invoke(ZNet.instance, null)) == sender;
				}
			}
			catch (Exception ex)
			{
				if (!warned)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)("Cannot check local voice UID: " + ex.Message));
					warned = true;
				}
			}
			return false;
		}

		private void CaptureAndSend()
		{
			//IL_01e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e9: Expected O, but got Unknown
			//IL_01f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0209: Unknown result type (might be due to invalid IL or missing references)
			//IL_0217: Unknown result type (might be due to invalid IL or missing references)
			//IL_0225: Unknown result type (might be due to invalid IL or missing references)
			StartMicrophone();
			if ((Object)(object)microphone == (Object)null || invokeRpc == null)
			{
				return;
			}
			int position = Microphone.GetPosition(micName);
			if (position < 0)
			{
				return;
			}
			int samples = microphone.samples;
			int num = (position - lastMicPosition + samples) % samples;
			if (num < 1600)
			{
				return;
			}
			if (num > 8000)
			{
				lastMicPosition = (position - 1600 + samples) % samples;
			}
			int num2 = Math.Max(1, microphone.channels);
			if (captureBufferChannels != num2 || captureInterleaved == null || captureInterleaved.Length != 1600 * num2)
			{
				captureInterleaved = new float[1600 * num2];
				captureBufferChannels = num2;
			}
			if (captureSamples == null || captureSamples.Length != 1600)
			{
				captureSamples = new float[1600];
			}
			if (captureEncoded == null || captureEncoded.Length != 1600)
			{
				captureEncoded = new byte[1600];
			}
			if (!microphone.GetData(captureInterleaved, lastMicPosition))
			{
				return;
			}
			lastMicPosition = (lastMicPosition + 1600) % samples;
			int value = microphoneChannel.Value;
			int num3 = Mathf.Clamp(value - 1, 0, num2 - 1);
			for (int i = 0; i < 1600; i++)
			{
				float num4 = 0f;
				int num5 = i * num2;
				for (int j = 0; j < num2; j++)
				{
					num4 += captureInterleaved[num5 + j];
				}
				captureSamples[i] = ((value == 0) ? (num4 / (float)num2) : captureInterleaved[num5 + num3]);
			}
			for (int k = 0; k < 1600; k++)
			{
				captureEncoded[k] = MuLawEncode(captureSamples[k]);
			}
			try
			{
				ZPackage val = new ZPackage();
				Vector3 position2 = ((Component)Player.m_localPlayer).transform.position;
				val.Write(sessionToken);
				val.Write(position2.x);
				val.Write(position2.y);
				val.Write(position2.z);
				val.Write(captureEncoded.Length);
				val.Write(captureEncoded);
				invokeRpc.Invoke(registeredOn, new object[3]
				{
					0L,
					"PPV_Audio_018",
					new object[1] { val }
				});
			}
			catch (Exception ex)
			{
				if (!warned)
				{
					((BaseUnityPlugin)this).Logger.LogError((object)("Voice send failed: " + ex));
					warned = true;
				}
			}
		}

		private void Receive(long sender, ZPackage packet)
		{
			//IL_005c: 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_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: 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_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			if (packet == null || (Object)(object)Player.m_localPlayer == (Object)null)
			{
				return;
			}
			try
			{
				string text = packet.ReadString();
				if (text == sessionToken || string.IsNullOrEmpty(text) || text.Length != 32)
				{
					return;
				}
				Vector3 val = default(Vector3);
				((Vector3)(ref val))..ctor(packet.ReadSingle(), packet.ReadSingle(), packet.ReadSingle());
				if (float.IsNaN(val.x) || float.IsNaN(val.y) || float.IsNaN(val.z) || float.IsInfinity(val.x) || float.IsInfinity(val.y) || float.IsInfinity(val.z) || Vector3.Distance(val, ((Component)Player.m_localPlayer).transform.position) > range.Value + 2f || packet.ReadInt() != 1600)
				{
					return;
				}
				byte[] array = packet.ReadByteArray();
				if (array != null && array.Length == 1600)
				{
					if (!speakers.TryGetValue(sender, out var value))
					{
						value = new Speaker(sender, 16000, range.Value);
						speakers[sender] = value;
					}
					((Component)value.source).transform.position = val;
					value.lastLocated = Time.unscaledTime;
					value.Push(array, Mathf.Clamp(voiceGain.Value, 0.1f, 24f));
				}
			}
			catch (Exception ex)
			{
				if (!warned)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)("Voice receive failed: " + ex));
					warned = true;
				}
			}
		}

		private bool FindPeerPosition(long uid, out Vector3 position)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: 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_00a2: Unknown result type (might be due to invalid IL or missing references)
			position = Vector3.zero;
			if ((Object)(object)ZNet.instance == (Object)null)
			{
				return false;
			}
			try
			{
				MethodInfo method = typeof(ZNet).GetMethod("GetPeer", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(long) }, null);
				object obj = ((method != null) ? method.Invoke(ZNet.instance, new object[1] { uid }) : null);
				if (obj == null)
				{
					return false;
				}
				FieldInfo field = obj.GetType().GetField("m_refPos", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field == null)
				{
					return false;
				}
				position = (Vector3)field.GetValue(obj);
				return true;
			}
			catch
			{
				return false;
			}
		}

		private static byte MuLawEncode(float value)
		{
			int num = Mathf.Clamp(Mathf.RoundToInt(value * 32767f), -32768, 32767);
			int num2 = (num >> 8) & 0x80;
			if (num2 != 0)
			{
				num = -num;
			}
			num = Math.Min(32635, num) + 132;
			int num3 = 7;
			int num4 = 16384;
			while (num3 > 0 && (num & num4) == 0)
			{
				num3--;
				num4 >>= 1;
			}
			int num5 = (num >> num3 + 3) & 0xF;
			return (byte)(~(num2 | (num3 << 4) | num5));
		}

		private static float MuLawDecode(byte encoded)
		{
			int num = ~encoded & 0xFF;
			int num2 = ((num & 0xF) << 3) + 132;
			num2 <<= (num >> 4) & 7;
			return Mathf.Clamp((float)(((num & 0x80) != 0) ? (132 - num2) : (num2 - 132)) / 32768f, -1f, 1f);
		}

		private void OnDestroy()
		{
			if ((Object)(object)micIconTexture != (Object)null)
			{
				Object.Destroy((Object)(object)micIconTexture);
				micIconTexture = null;
			}
			StopMicrophone();
			foreach (Speaker value in speakers.Values)
			{
				if (Object.op_Implicit((Object)(object)value.source))
				{
					Object.Destroy((Object)(object)((Component)value.source).gameObject);
				}
			}
			speakers.Clear();
			if ((Object)(object)playerControlsCanvas != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)playerControlsCanvas).gameObject);
			}
			nativeSliders.Clear();
		}
	}
}