Decompiled source of LastWords v1.0.1

plugins/LastWords/aj.LastWords.dll

Decompiled 2 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using Dissonance;
using GameNetcodeStuff;
using HarmonyLib;
using LastWords.Configuration;
using Microsoft.CodeAnalysis;
using TMPro;
using Unity.Collections;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.Rendering.HighDefinition;

[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("aj.LastWords")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.1.0")]
[assembly: AssemblyInformationalVersion("1.0.1")]
[assembly: AssemblyProduct("LastWords")]
[assembly: AssemblyTitle("aj.LastWords")]
[assembly: AssemblyMetadata("AI_Assisted_Creation", "This assembly was created with the assistance of generative AI (code); see the README credits for which assets.")]
[assembly: AssemblyMetadata("AI_Model_Vendor", "Anthropic")]
[assembly: AssemblyVersion("1.0.1.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 LastWords
{
	internal class LastWordsTestKeys : MonoBehaviour
	{
		private const Key DieKey = (Key)99;

		public static void Install()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Expected O, but got Unknown
			GameObject val = new GameObject("LastWordsTestKeys")
			{
				hideFlags = (HideFlags)61
			};
			Object.DontDestroyOnLoad((Object)val);
			val.AddComponent<LastWordsTestKeys>();
			Plugin.Logger.LogInfo((object)"Test keys on: F6 drops you dead.");
		}

		private void Update()
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: 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)
			Keyboard current = Keyboard.current;
			if (current != null && ((ButtonControl)current[(Key)99]).wasPressedThisFrame)
			{
				PlayerControllerB val = (((Object)(object)GameNetworkManager.Instance != (Object)null) ? GameNetworkManager.Instance.localPlayerController : null);
				if ((Object)(object)val != (Object)null && !val.isPlayerDead && val.isPlayerControlled)
				{
					val.KillPlayer(Vector3.up, true, (CauseOfDeath)0, 0, default(Vector3), false);
				}
			}
		}
	}
	[HarmonyPatch]
	internal static class Patches
	{
		[HarmonyPatch(typeof(StartOfRound), "Start")]
		[HarmonyPostfix]
		private static void StartOfRoundStarted(StartOfRound __instance)
		{
			SpiritNet.Register();
			((Component)__instance).gameObject.AddComponent<SpiritRunner>();
		}

		[HarmonyPatch(typeof(StartOfRound), "UpdatePlayerVoiceEffects")]
		[HarmonyPostfix]
		private static void VoicesReset()
		{
			Spirit.ReapplyVoices();
		}
	}
	[BepInPlugin("aj.LastWords", "LastWords", "1.0.1")]
	public class Plugin : BaseUnityPlugin
	{
		internal static ManualLogSource Logger;

		internal static GameObject? SpiritPrefab;

		internal static PluginConfig BoundConfig { get; private set; }

		private void Awake()
		{
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			Logger = ((BaseUnityPlugin)this).Logger;
			BoundConfig = new PluginConfig(((BaseUnityPlugin)this).Config);
			AssetBundle val = AssetBundle.LoadFromFile(Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location), "lastwordsassets"));
			if ((Object)(object)val == (Object)null)
			{
				Logger.LogError((object)"Failed to load the lastwordsassets bundle; spirits will have no glow.");
			}
			else
			{
				SpiritPrefab = val.LoadAsset<GameObject>("LastWordsSpirit");
			}
			SpiritView.CreateKey(BoundConfig.SpeakKey.Value);
			new Harmony("aj.LastWords").PatchAll(typeof(Patches));
			if (BoundConfig.TestKeys.Value)
			{
				LastWordsTestKeys.Install();
			}
			Logger.LogInfo((object)"LastWords 1.0.1 loaded.");
		}
	}
	internal class Spirit
	{
		internal const int Head = 0;

		internal const int Chest = 5;

		private const float LateEndGrace = 2f;

		private static readonly Dictionary<int, Spirit> active = new Dictionary<int, Spirit>();

		private readonly PlayerControllerB player;

		private readonly DeadBodyInfo body;

		private readonly float endsAt;

		private readonly SpiritGlow? glow;

		private SpiritVoice? voice;

		private bool muted;

		private static PlayerControllerB? Local
		{
			get
			{
				if (!((Object)(object)GameNetworkManager.Instance != (Object)null))
				{
					return null;
				}
				return GameNetworkManager.Instance.localPlayerController;
			}
		}

		private Spirit(PlayerControllerB player, DeadBodyInfo body, float seconds)
		{
			this.player = player;
			this.body = body;
			endsAt = Time.time + seconds;
			glow = SpiritGlow.Spawn(player, body);
		}

		internal static DeadBodyInfo? BodyOf(PlayerControllerB? p)
		{
			DeadBodyInfo val = (((Object)(object)p != (Object)null && p.isPlayerDead) ? p.deadBody : null);
			if (!((Object)(object)val != (Object)null) || val.deactivated || !((Component)val).gameObject.activeInHierarchy || val.bodyParts.Length <= 5)
			{
				return null;
			}
			return val;
		}

		internal static void Begin(int index, float seconds)
		{
			PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts;
			if (index < 0 || index >= allPlayerScripts.Length)
			{
				return;
			}
			Stop(index);
			PlayerControllerB val = allPlayerScripts[index];
			DeadBodyInfo val2 = BodyOf(val);
			if (!((Object)(object)val2 == (Object)null))
			{
				Spirit spirit = new Spirit(val, val2, seconds);
				active[index] = spirit;
				spirit.UpdateVoice();
				if ((Object)(object)val == (Object)(object)Local)
				{
					SpiritView.Speaking(val2, seconds);
				}
			}
		}

		internal static void Finish(int index, float cooldown)
		{
			Stop(index);
			PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts;
			if (index >= 0 && index < allPlayerScripts.Length && (Object)(object)allPlayerScripts[index] == (Object)(object)Local)
			{
				SpiritView.Resting(cooldown);
			}
		}

		internal static void Clear()
		{
			foreach (Spirit value in active.Values)
			{
				if ((Object)(object)value.glow != (Object)null)
				{
					Object.Destroy((Object)(object)((Component)value.glow).gameObject);
				}
			}
			active.Clear();
		}

		private static void Stop(int index)
		{
			if (active.TryGetValue(index, out Spirit value))
			{
				active.Remove(index);
				if ((Object)(object)value.glow != (Object)null)
				{
					value.glow.FadeOut();
				}
				if (value.voice != null)
				{
					value.voice.Restore();
				}
				if (value.muted)
				{
					StartOfRound.Instance.UpdatePlayerVoiceEffects();
				}
				if ((Object)(object)value.player == (Object)(object)Local)
				{
					SpiritView.StoppedSpeaking();
				}
			}
		}

		internal static void TickAll()
		{
			if (active.Count == 0)
			{
				return;
			}
			foreach (KeyValuePair<int, Spirit> item in active.ToList())
			{
				Spirit value = item.Value;
				if (Time.time > value.endsAt + 2f || (Object)(object)BodyOf(value.player) != (Object)(object)value.body)
				{
					Stop(item.Key);
				}
				else
				{
					value.UpdateVoice();
				}
			}
		}

		internal static void LateTickAll()
		{
			foreach (Spirit value in active.Values)
			{
				value.voice?.Place();
			}
		}

		internal static void ReapplyVoices()
		{
			foreach (Spirit value in active.Values)
			{
				value.voice?.Apply();
				if (value.muted)
				{
					value.Mute();
				}
			}
		}

		private void UpdateVoice()
		{
			PlayerControllerB local = Local;
			int num;
			int num2;
			if ((Object)(object)local != (Object)null)
			{
				num = (((Object)(object)local != (Object)(object)player) ? 1 : 0);
				if (num != 0)
				{
					num2 = ((!local.isPlayerDead) ? 1 : 0);
					goto IL_002d;
				}
			}
			else
			{
				num = 0;
			}
			num2 = 0;
			goto IL_002d;
			IL_002d:
			bool flag = (byte)num2 != 0;
			bool num3 = num != 0 && local.isPlayerDead;
			if (voice != null && (!flag || !voice.IsCurrent))
			{
				SpiritVoice? spiritVoice = voice;
				voice = null;
				spiritVoice.Restore();
			}
			if (!num3 && muted)
			{
				muted = false;
				StartOfRound.Instance.UpdatePlayerVoiceEffects();
			}
			if (flag && voice == null)
			{
				voice = SpiritVoice.Create(player, body);
				voice?.Apply();
			}
			if (num3 && !muted && player.voicePlayerState != null)
			{
				muted = true;
				Mute();
			}
		}

		private void Mute()
		{
			if (player.voicePlayerState != null)
			{
				player.voicePlayerState.Volume = 0f;
			}
		}
	}
	internal class SpiritGlow : MonoBehaviour
	{
		private const float FadeSeconds = 0.5f;

		private const float LightHeight = 0.3f;

		private const float SoulHeight = 0.3f;

		private const float BobHeight = 0.02f;

		private const float BobSpeed = 1.6f;

		private const float ThreadStart = 0.05f;

		private const float BreathDepth = 0.1f;

		private const float BreathSpeed = 1.8f;

		private const float Quiet = 0.005f;

		private const float Loud = 0.15f;

		private const float RiseSpeed = 18f;

		private const float FallSpeed = 7f;

		private const float SoulSwell = 0.6f;

		private const float SpriteSwell = 1.5f;

		private const float LightSwell = 1.2f;

		private PlayerControllerB speaker;

		private DeadBodyInfo body;

		private HDAdditionalLightData? lightData;

		private float lightIntensity;

		private Transform soul;

		private Transform thread;

		private Transform glowLight;

		private Transform[] soulSprites;

		private Vector3 soulScale;

		private Renderer[] sprites;

		private Color[] spriteColors;

		private MaterialPropertyBlock block;

		private float fade;

		private float voice;

		private bool ending;

		private static readonly int Emission = Shader.PropertyToID("_EmissiveColor");

		internal static SpiritGlow? Spawn(PlayerControllerB speaker, DeadBodyInfo body)
		{
			if ((Object)(object)Plugin.SpiritPrefab == (Object)null)
			{
				return null;
			}
			SpiritGlow spiritGlow = Object.Instantiate<GameObject>(Plugin.SpiritPrefab).AddComponent<SpiritGlow>();
			spiritGlow.Init(speaker, body);
			return spiritGlow;
		}

		private void Init(PlayerControllerB player, DeadBodyInfo deadBody)
		{
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Expected O, but got Unknown
			speaker = player;
			body = deadBody;
			glowLight = ((Component)this).transform.Find("Glow");
			soul = ((Component)this).transform.Find("Soul");
			thread = ((Component)this).transform.Find("Thread");
			soulScale = soul.localScale;
			soulSprites = (Transform[])(object)new Transform[soul.childCount];
			for (int i = 0; i < soulSprites.Length; i++)
			{
				soulSprites[i] = soul.GetChild(i);
			}
			lightData = ((Component)glowLight).GetComponent<HDAdditionalLightData>();
			lightIntensity = (((Object)(object)lightData != (Object)null) ? lightData.intensity : 0f);
			sprites = ((Component)this).GetComponentsInChildren<Renderer>();
			spriteColors = (Color[])(object)new Color[sprites.Length];
			for (int j = 0; j < sprites.Length; j++)
			{
				spriteColors[j] = sprites[j].sharedMaterial.GetColor(Emission);
			}
			block = new MaterialPropertyBlock();
			Follow();
		}

		internal void FadeOut()
		{
			ending = true;
		}

		private void LateUpdate()
		{
			if ((Object)(object)body == (Object)null)
			{
				ending = true;
			}
			fade = Mathf.MoveTowards(fade, ending ? 0f : 1f, Time.deltaTime / 0.5f);
			if (ending && fade <= 0f)
			{
				Object.Destroy((Object)(object)((Component)this).gameObject);
				return;
			}
			float num = ((ending || (Object)(object)speaker == (Object)null) ? 0f : Mathf.Sqrt(Mathf.InverseLerp(0.005f, 0.15f, SpiritVoice.Loudness(speaker))));
			voice = Mathf.Lerp(voice, num, 1f - Mathf.Exp((0f - ((num > voice) ? 18f : 7f)) * Time.deltaTime));
			if ((Object)(object)body != (Object)null)
			{
				Follow();
			}
		}

		private void Follow()
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: 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_0035: 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_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: 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_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0204: Unknown result type (might be due to invalid IL or missing references)
			//IL_0206: Unknown result type (might be due to invalid IL or missing references)
			//IL_020b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0213: Unknown result type (might be due to invalid IL or missing references)
			//IL_0215: Unknown result type (might be due to invalid IL or missing references)
			//IL_021c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0221: Unknown result type (might be due to invalid IL or missing references)
			//IL_0231: Unknown result type (might be due to invalid IL or missing references)
			//IL_0236: 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_023e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0243: Unknown result type (might be due to invalid IL or missing references)
			//IL_0248: Unknown result type (might be due to invalid IL or missing references)
			//IL_0272: 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_027f: Unknown result type (might be due to invalid IL or missing references)
			//IL_028d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0294: Unknown result type (might be due to invalid IL or missing references)
			//IL_025e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0260: Unknown result type (might be due to invalid IL or missing references)
			//IL_0262: Unknown result type (might be due to invalid IL or missing references)
			Vector3 position = body.bodyParts[5].position;
			((Component)this).transform.position = position;
			glowLight.position = position + Vector3.up * 0.3f;
			soul.position = position + Vector3.up * (0.3f + 0.02f * Mathf.Sin(Time.time * 1.6f));
			soul.localScale = soulScale * (1f + 0.6f * voice);
			float num = 1f - 0.1f * (1f - voice) * 0.5f * (1f + Mathf.Sin(Time.time * 1.8f));
			if ((Object)(object)lightData != (Object)null)
			{
				lightData.intensity = lightIntensity * fade * num * (1f + 1.2f * voice);
			}
			for (int i = 0; i < sprites.Length; i++)
			{
				block.SetColor(Emission, spriteColors[i] * (fade * num * (1f + 1.5f * voice)));
				sprites[i].SetPropertyBlock(block);
			}
			Camera val = (((Object)(object)StartOfRound.Instance != (Object)null) ? StartOfRound.Instance.activeCamera : Camera.main);
			if (!((Object)(object)val == (Object)null))
			{
				Vector3 position2 = ((Component)val).transform.position;
				Transform[] array = soulSprites;
				foreach (Transform obj in array)
				{
					obj.rotation = Quaternion.LookRotation(obj.position - position2);
				}
				Vector3 val2 = position + Vector3.up * 0.05f;
				Vector3 val3 = soul.position - val2;
				thread.position = val2 + val3 * 0.5f;
				Vector3 val4 = Vector3.ProjectOnPlane(thread.position - position2, ((Vector3)(ref val3)).normalized);
				if (((Vector3)(ref val4)).sqrMagnitude > 1E-06f)
				{
					thread.rotation = Quaternion.LookRotation(val4, val3);
				}
				Vector3 localScale = thread.localScale;
				thread.localScale = new Vector3(localScale.x, ((Vector3)(ref val3)).magnitude, localScale.z);
			}
		}
	}
	internal static class SpiritNet
	{
		internal enum Refusal : byte
		{
			NoBody,
			Speaking,
			Resting
		}

		[CompilerGenerated]
		private static class <>O
		{
			public static HandleNamedMessageDelegate <0>__OnRequest;

			public static HandleNamedMessageDelegate <1>__OnStart;

			public static HandleNamedMessageDelegate <2>__OnEnd;

			public static HandleNamedMessageDelegate <3>__OnRefused;
		}

		private const string RequestMessage = "aj.LastWords.Request";

		private const string StartMessage = "aj.LastWords.Start";

		private const string EndMessage = "aj.LastWords.End";

		private const string RefusedMessage = "aj.LastWords.Refused";

		private static readonly Dictionary<int, float> speakingUntil = new Dictionary<int, float>();

		private static readonly Dictionary<int, float> restingUntil = new Dictionary<int, float>();

		private static CustomMessagingManager? Messages
		{
			get
			{
				if (!((Object)(object)NetworkManager.Singleton != (Object)null))
				{
					return null;
				}
				return NetworkManager.Singleton.CustomMessagingManager;
			}
		}

		private static bool IsHost
		{
			get
			{
				if ((Object)(object)NetworkManager.Singleton != (Object)null)
				{
					return NetworkManager.Singleton.IsServer;
				}
				return false;
			}
		}

		internal static void Register()
		{
			//IL_0043: 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_004e: Expected O, but got Unknown
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Expected O, but got Unknown
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Expected O, but got Unknown
			//IL_00b5: 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_00c0: Expected O, but got Unknown
			speakingUntil.Clear();
			restingUntil.Clear();
			CustomMessagingManager messages = Messages;
			if (messages == null)
			{
				Plugin.Logger.LogError((object)"No network messaging; Last Words is off for this lobby.");
				return;
			}
			object obj = <>O.<0>__OnRequest;
			if (obj == null)
			{
				HandleNamedMessageDelegate val = OnRequest;
				<>O.<0>__OnRequest = val;
				obj = (object)val;
			}
			messages.RegisterNamedMessageHandler("aj.LastWords.Request", (HandleNamedMessageDelegate)obj);
			object obj2 = <>O.<1>__OnStart;
			if (obj2 == null)
			{
				HandleNamedMessageDelegate val2 = OnStart;
				<>O.<1>__OnStart = val2;
				obj2 = (object)val2;
			}
			messages.RegisterNamedMessageHandler("aj.LastWords.Start", (HandleNamedMessageDelegate)obj2);
			object obj3 = <>O.<2>__OnEnd;
			if (obj3 == null)
			{
				HandleNamedMessageDelegate val3 = OnEnd;
				<>O.<2>__OnEnd = val3;
				obj3 = (object)val3;
			}
			messages.RegisterNamedMessageHandler("aj.LastWords.End", (HandleNamedMessageDelegate)obj3);
			object obj4 = <>O.<3>__OnRefused;
			if (obj4 == null)
			{
				HandleNamedMessageDelegate val4 = OnRefused;
				<>O.<3>__OnRefused = val4;
				obj4 = (object)val4;
			}
			messages.RegisterNamedMessageHandler("aj.LastWords.Refused", (HandleNamedMessageDelegate)obj4);
		}

		internal static void Unregister()
		{
			CustomMessagingManager messages = Messages;
			if (messages != null)
			{
				messages.UnregisterNamedMessageHandler("aj.LastWords.Request");
				messages.UnregisterNamedMessageHandler("aj.LastWords.Start");
				messages.UnregisterNamedMessageHandler("aj.LastWords.End");
				messages.UnregisterNamedMessageHandler("aj.LastWords.Refused");
			}
			speakingUntil.Clear();
			restingUntil.Clear();
		}

		internal unsafe static void Ask(int player)
		{
			//IL_002a: 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_003e: Unknown result type (might be due to invalid IL or missing references)
			if (IsHost)
			{
				HostRequest(player, 0uL);
				return;
			}
			CustomMessagingManager messages = Messages;
			if (messages == null)
			{
				return;
			}
			FastBufferWriter val = default(FastBufferWriter);
			((FastBufferWriter)(ref val))..ctor(4, (Allocator)2, -1);
			try
			{
				((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref player, default(ForPrimitives));
				messages.SendNamedMessage("aj.LastWords.Request", 0uL, val, (NetworkDelivery)2);
			}
			finally
			{
				((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose();
			}
		}

		private static void OnRequest(ulong sender, FastBufferReader reader)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			if (IsHost)
			{
				int player = default(int);
				((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref player, default(ForPrimitives));
				HostRequest(player, sender);
			}
		}

		private static void HostRequest(int player, ulong sender)
		{
			PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts;
			if (player < 0 || player >= allPlayerScripts.Length)
			{
				return;
			}
			PlayerControllerB val = allPlayerScripts[player];
			if (val.actualClientId == sender && val.isPlayerDead && !StartOfRound.Instance.shipIsLeaving)
			{
				if ((Object)(object)Spirit.BodyOf(val) == (Object)null)
				{
					Refuse(sender, Refusal.NoBody, 0f);
					return;
				}
				if (speakingUntil.ContainsKey(player))
				{
					Refuse(sender, Refusal.Speaking, 0f);
					return;
				}
				if (restingUntil.TryGetValue(player, out var value) && Time.time < value)
				{
					Refuse(sender, Refusal.Resting, value - Time.time);
					return;
				}
				PluginConfig boundConfig = Plugin.BoundConfig;
				float num = Mathf.Max(1f, boundConfig.MinSeconds.Value);
				float num2 = Random.Range(num, Mathf.Max(num, boundConfig.MaxSeconds.Value));
				speakingUntil[player] = Time.time + num2;
				Broadcast("aj.LastWords.Start", player, num2);
			}
		}

		internal static void HostTick()
		{
			if (!IsHost || speakingUntil.Count == 0)
			{
				return;
			}
			PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts;
			foreach (int item in speakingUntil.Keys.ToList())
			{
				PlayerControllerB p = allPlayerScripts[item];
				if (!(Time.time < speakingUntil[item]) || !((Object)(object)Spirit.BodyOf(p) != (Object)null) || StartOfRound.Instance.shipIsLeaving)
				{
					speakingUntil.Remove(item);
					float num = Mathf.Max(0f, Plugin.BoundConfig.CooldownSeconds.Value);
					restingUntil[item] = Time.time + num;
					Broadcast("aj.LastWords.End", item, num);
				}
			}
		}

		private static void OnStart(ulong sender, FastBufferReader reader)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			if (sender == 0L)
			{
				int index = default(int);
				((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref index, default(ForPrimitives));
				float seconds = default(float);
				((FastBufferReader)(ref reader)).ReadValueSafe<float>(ref seconds, default(ForPrimitives));
				Spirit.Begin(index, seconds);
			}
		}

		private static void OnEnd(ulong sender, FastBufferReader reader)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			if (sender == 0L)
			{
				int index = default(int);
				((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref index, default(ForPrimitives));
				float cooldown = default(float);
				((FastBufferReader)(ref reader)).ReadValueSafe<float>(ref cooldown, default(ForPrimitives));
				Spirit.Finish(index, cooldown);
			}
		}

		private static void OnRefused(ulong sender, FastBufferReader reader)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			if (sender == 0L)
			{
				byte why = default(byte);
				((FastBufferReader)(ref reader)).ReadValueSafe<byte>(ref why, default(ForPrimitives));
				float resting = default(float);
				((FastBufferReader)(ref reader)).ReadValueSafe<float>(ref resting, default(ForPrimitives));
				SpiritView.Refused((Refusal)why, resting);
			}
		}

		private unsafe static void Broadcast(string message, int player, float value)
		{
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			CustomMessagingManager messages = Messages;
			List<ulong> list = NetworkManager.Singleton.ConnectedClientsIds.Where((ulong id) => id != 0).ToList();
			if (messages != null && list.Count > 0)
			{
				FastBufferWriter val = default(FastBufferWriter);
				((FastBufferWriter)(ref val))..ctor(8, (Allocator)2, -1);
				try
				{
					((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref player, default(ForPrimitives));
					((FastBufferWriter)(ref val)).WriteValueSafe<float>(ref value, default(ForPrimitives));
					messages.SendNamedMessage(message, (IReadOnlyList<ulong>)list, val, (NetworkDelivery)2);
				}
				finally
				{
					((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose();
				}
			}
			if (message == "aj.LastWords.Start")
			{
				Spirit.Begin(player, value);
			}
			else
			{
				Spirit.Finish(player, value);
			}
		}

		private unsafe static void Refuse(ulong client, Refusal why, float resting)
		{
			//IL_0027: 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_0039: 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_004c: Unknown result type (might be due to invalid IL or missing references)
			if (client == 0L)
			{
				SpiritView.Refused(why, resting);
				return;
			}
			CustomMessagingManager messages = Messages;
			if (messages == null)
			{
				return;
			}
			FastBufferWriter val = default(FastBufferWriter);
			((FastBufferWriter)(ref val))..ctor(5, (Allocator)2, -1);
			try
			{
				byte b = (byte)why;
				((FastBufferWriter)(ref val)).WriteValueSafe<byte>(ref b, default(ForPrimitives));
				((FastBufferWriter)(ref val)).WriteValueSafe<float>(ref resting, default(ForPrimitives));
				messages.SendNamedMessage("aj.LastWords.Refused", client, val, (NetworkDelivery)2);
			}
			finally
			{
				((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose();
			}
		}
	}
	[DefaultExecutionOrder(10000)]
	internal class SpiritRunner : MonoBehaviour
	{
		private void Update()
		{
			SpiritNet.HostTick();
			Spirit.TickAll();
			SpiritView.Tick();
		}

		private void LateUpdate()
		{
			Spirit.LateTickAll();
			SpiritView.LateTick();
		}

		private void OnDestroy()
		{
			Spirit.Clear();
			SpiritView.Reset();
			SpiritNet.Unregister();
		}
	}
	internal static class SpiritView
	{
		private enum Shown
		{
			Nothing,
			Speaking,
			Refused,
			NoBody,
			Resting,
			Ready
		}

		private const float ViewDistance = 2.5f;

		private const float ViewHeight = 0.2f;

		private const float RefusalSeconds = 2.5f;

		private const float LineGap = 30f;

		private const float LineScale = 0.8f;

		private static InputAction speakKey = null;

		private static TextMeshProUGUI? line;

		private static DeadBodyInfo? speakingFrom;

		private static float speakingUntil;

		private static float restingUntil;

		private static string refusal = "";

		private static float refusalUntil;

		private static Shown shown;

		private static int shownCount = -1;

		internal static void CreateKey(string path)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Expected O, but got Unknown
			speakKey = new InputAction("LastWordsSpeak", (InputActionType)1, path, (string)null, (string)null, (string)null);
			speakKey.Enable();
		}

		internal static void Speaking(DeadBodyInfo body, float seconds)
		{
			speakingFrom = body;
			speakingUntil = Time.time + seconds;
			refusal = "";
		}

		internal static void StoppedSpeaking()
		{
			speakingFrom = null;
		}

		internal static void Resting(float cooldown)
		{
			restingUntil = Time.time + cooldown;
		}

		internal static void Refused(SpiritNet.Refusal why, float resting)
		{
			object obj;
			switch (why)
			{
			case SpiritNet.Refusal.Resting:
				restingUntil = Time.time + resting;
				return;
			default:
				obj = "";
				break;
			case SpiritNet.Refusal.NoBody:
				obj = "Your body is gone";
				break;
			}
			refusal = (string)obj;
			refusalUntil = Time.time + 2.5f;
		}

		internal static void Reset()
		{
			speakingFrom = null;
			speakingUntil = (restingUntil = (refusalUntil = 0f));
			refusal = "";
			line = null;
		}

		internal static void Tick()
		{
			PlayerControllerB val = (((Object)(object)GameNetworkManager.Instance != (Object)null) ? GameNetworkManager.Instance.localPlayerController : null);
			if ((Object)(object)val == (Object)null || (Object)(object)HUDManager.Instance == (Object)null)
			{
				return;
			}
			EnsureLine();
			if (!val.isPlayerDead)
			{
				Show(Shown.Nothing, 0);
				return;
			}
			bool flag = (Object)(object)Spirit.BodyOf(val) != (Object)null;
			if ((Object)(object)speakingFrom == (Object)null && Time.time >= restingUntil && flag && speakKey.WasPressedThisFrame() && !val.isTypingChat && !val.quickMenuManager.isMenuOpen)
			{
				SpiritNet.Ask((int)val.playerClientId);
			}
			if ((Object)(object)speakingFrom != (Object)null)
			{
				Show(Shown.Speaking, Mathf.CeilToInt(Mathf.Max(0f, speakingUntil - Time.time)));
			}
			else if (Time.time < refusalUntil && refusal != "")
			{
				Show(Shown.Refused, 0);
			}
			else if (!flag)
			{
				Show(Shown.NoBody, 0);
			}
			else if (Time.time < restingUntil)
			{
				Show(Shown.Resting, Mathf.CeilToInt(restingUntil - Time.time));
			}
			else
			{
				Show(Shown.Ready, 0);
			}
		}

		private static void Show(Shown what, int count)
		{
			if (!((Object)(object)line == (Object)null) && (what != shown || count != shownCount))
			{
				shown = what;
				shownCount = count;
				TextMeshProUGUI val = line;
				((TMP_Text)val).text = what switch
				{
					Shown.Speaking => $"Your spirit speaks… {count}", 
					Shown.Refused => refusal, 
					Shown.NoBody => "Your body is gone", 
					Shown.Resting => $"Your spirit rests… {count}", 
					Shown.Ready => "[" + InputActionRebindingExtensions.GetBindingDisplayString(speakKey, (DisplayStringOptions)0, (string)null) + "] Speak from your body", 
					_ => "", 
				};
			}
		}

		internal static void LateTick()
		{
			//IL_004e: 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_005d: Unknown result type (might be due to invalid IL or missing references)
			//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_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			PlayerControllerB val = (((Object)(object)GameNetworkManager.Instance != (Object)null) ? GameNetworkManager.Instance.localPlayerController : null);
			if (!((Object)(object)speakingFrom == (Object)null) && !((Object)(object)val == (Object)null) && val.isPlayerDead)
			{
				Transform spectateCameraPivot = val.spectateCameraPivot;
				spectateCameraPivot.position = speakingFrom.bodyParts[5].position + Vector3.up * 0.2f;
				Transform transform = ((Component)StartOfRound.Instance.spectateCamera).transform;
				Vector3 val2 = -spectateCameraPivot.forward;
				float num = 2.5f;
				RaycastHit val3 = default(RaycastHit);
				if (Physics.SphereCast(spectateCameraPivot.position, 0.2f, val2, ref val3, 2.5f, StartOfRound.Instance.collidersAndRoomMaskAndDefault, (QueryTriggerInteraction)1))
				{
					num = Mathf.Max(0.4f, ((RaycastHit)(ref val3)).distance - 0.1f);
				}
				transform.position = spectateCameraPivot.position + val2 * num;
				transform.LookAt(spectateCameraPivot);
			}
		}

		private static void EnsureLine()
		{
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: 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)
			if (!((Object)(object)line != (Object)null))
			{
				TextMeshProUGUI spectatingPlayerText = HUDManager.Instance.spectatingPlayerText;
				if (!((Object)(object)spectatingPlayerText == (Object)null))
				{
					line = Object.Instantiate<TextMeshProUGUI>(spectatingPlayerText, ((TMP_Text)spectatingPlayerText).transform.parent);
					((Object)line).name = "LastWordsLine";
					((TMP_Text)line).text = "";
					((TMP_Text)line).fontSize = ((TMP_Text)spectatingPlayerText).fontSize * 0.8f;
					((TMP_Text)line).rectTransform.anchoredPosition = ((TMP_Text)spectatingPlayerText).rectTransform.anchoredPosition + Vector2.down * 30f;
					shown = Shown.Nothing;
					shownCount = -1;
				}
			}
		}
	}
	internal class SpiritVoice
	{
		private readonly struct ChorusSettings
		{
			private readonly bool enabled;

			private readonly float dry;

			private readonly float wet1;

			private readonly float wet2;

			private readonly float wet3;

			private readonly float delay;

			private readonly float rate;

			private readonly float depth;

			private ChorusSettings(AudioChorusFilter c)
			{
				enabled = ((Behaviour)c).enabled;
				dry = c.dryMix;
				wet1 = c.wetMix1;
				wet2 = c.wetMix2;
				wet3 = c.wetMix3;
				delay = c.delay;
				rate = c.rate;
				depth = c.depth;
			}

			internal static ChorusSettings From(AudioChorusFilter c)
			{
				return new ChorusSettings(c);
			}

			internal void ApplyTo(AudioChorusFilter c)
			{
				c.dryMix = dry;
				c.wetMix1 = wet1;
				c.wetMix2 = wet2;
				c.wetMix3 = wet3;
				c.delay = delay;
				c.rate = rate;
				c.depth = depth;
				((Behaviour)c).enabled = enabled;
			}
		}

		private readonly struct ReverbSettings
		{
			private readonly AudioReverbPreset preset;

			private readonly float dry;

			private readonly float room;

			private readonly float roomHF;

			private readonly float roomLF;

			private readonly float decay;

			private readonly float decayHFRatio;

			private readonly float reflections;

			private readonly float reflectionsDelay;

			private readonly float level;

			private readonly float delay;

			private readonly float hfReference;

			private readonly float lfReference;

			private readonly float diffusion;

			private readonly float density;

			private ReverbSettings(AudioReverbFilter r)
			{
				//IL_0002: Unknown result type (might be due to invalid IL or missing references)
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				preset = r.reverbPreset;
				dry = r.dryLevel;
				room = r.room;
				roomHF = r.roomHF;
				roomLF = r.roomLF;
				decay = r.decayTime;
				decayHFRatio = r.decayHFRatio;
				reflections = r.reflectionsLevel;
				reflectionsDelay = r.reflectionsDelay;
				level = r.reverbLevel;
				delay = r.reverbDelay;
				hfReference = r.hfReference;
				lfReference = r.lfReference;
				diffusion = r.diffusion;
				density = r.density;
			}

			internal static ReverbSettings From(AudioReverbFilter r)
			{
				return new ReverbSettings(r);
			}

			internal void ApplyTo(AudioReverbFilter r)
			{
				//IL_0002: Unknown result type (might be due to invalid IL or missing references)
				//IL_000d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0014: Invalid comparison between Unknown and I4
				r.reverbPreset = preset;
				if ((int)preset == 27)
				{
					r.dryLevel = dry;
					r.room = room;
					r.roomHF = roomHF;
					r.roomLF = roomLF;
					r.decayTime = decay;
					r.decayHFRatio = decayHFRatio;
					r.reflectionsLevel = reflections;
					r.reflectionsDelay = reflectionsDelay;
					r.reverbLevel = level;
					r.reverbDelay = delay;
					r.hfReference = hfReference;
					r.lfReference = lfReference;
					r.diffusion = diffusion;
					r.density = density;
				}
			}
		}

		private const float HighPassHz = 380f;

		private const float ChorusDry = 0.7f;

		private const float ChorusWet = 0.35f;

		private const float ChorusDelayMs = 24f;

		private const float ChorusRate = 0.5f;

		private const float ChorusDepth = 0.06f;

		private const float ReverbDry = 0f;

		private const float ReverbRoom = -250f;

		private const float ReverbRoomHF = -150f;

		private const float ReverbDecay = 3.4f;

		private const float ReverbDecayHFRatio = 1.25f;

		private const float ReverbReflections = -900f;

		private const float ReverbReflectionsDelay = 0.02f;

		private const float ReverbLevel = 250f;

		private const float ReverbDelay = 0.045f;

		private const float Pitch = 1.06f;

		private const float FullVolumeFraction = 0.15f;

		private readonly PlayerControllerB player;

		private readonly DeadBodyInfo body;

		private readonly PlayerVoiceIngameSettings settings;

		private readonly AudioSource source;

		private readonly AudioReverbFilter? reverb;

		private readonly AudioHighPassFilter? highPass;

		private readonly AudioLowPassFilter? lowPass;

		private readonly AudioChorusFilter chorus;

		private readonly bool addedChorus;

		private readonly ChorusSettings savedChorus;

		private readonly AudioRolloffMode savedRolloff;

		private readonly float savedMinDistance;

		private readonly float savedMaxDistance;

		private readonly AnimationCurve savedCurve;

		private readonly bool savedBypassEffects;

		private readonly bool savedBypassListenerEffects;

		private readonly bool savedHighPassOn;

		private readonly bool savedLowPassOn;

		private readonly bool savedReverbOn;

		private readonly float savedHighPassHz;

		private readonly float savedHighPassQ;

		private readonly ReverbSettings savedReverb;

		internal bool IsCurrent
		{
			get
			{
				if ((Object)(object)source != (Object)null && (Object)(object)settings != (Object)null)
				{
					return (Object)(object)player.currentVoiceChatAudioSource == (Object)(object)source;
				}
				return false;
			}
		}

		private SpiritVoice(PlayerControllerB player, DeadBodyInfo body, PlayerVoiceIngameSettings settings, AudioSource source)
		{
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			this.player = player;
			this.body = body;
			this.settings = settings;
			this.source = source;
			GameObject gameObject = ((Component)source).gameObject;
			reverb = gameObject.GetComponent<AudioReverbFilter>();
			highPass = gameObject.GetComponent<AudioHighPassFilter>();
			lowPass = gameObject.GetComponent<AudioLowPassFilter>();
			addedChorus = (Object)(object)gameObject.GetComponent<AudioChorusFilter>() == (Object)null;
			chorus = (addedChorus ? gameObject.AddComponent<AudioChorusFilter>() : gameObject.GetComponent<AudioChorusFilter>());
			savedChorus = ChorusSettings.From(chorus);
			savedRolloff = source.rolloffMode;
			savedMinDistance = source.minDistance;
			savedMaxDistance = source.maxDistance;
			savedCurve = source.GetCustomCurve((AudioSourceCurveType)0);
			savedBypassEffects = source.bypassEffects;
			savedBypassListenerEffects = source.bypassListenerEffects;
			savedHighPassOn = (Object)(object)highPass != (Object)null && ((Behaviour)highPass).enabled;
			savedHighPassHz = (((Object)(object)highPass != (Object)null) ? highPass.cutoffFrequency : 0f);
			savedHighPassQ = (((Object)(object)highPass != (Object)null) ? highPass.highpassResonanceQ : 1f);
			savedLowPassOn = (Object)(object)lowPass != (Object)null && ((Behaviour)lowPass).enabled;
			savedReverbOn = (Object)(object)reverb != (Object)null && ((Behaviour)reverb).enabled;
			savedReverb = (((Object)(object)reverb != (Object)null) ? ReverbSettings.From(reverb) : default(ReverbSettings));
		}

		internal static float Loudness(PlayerControllerB p)
		{
			VoicePlayerState val = p.voicePlayerState;
			DissonanceComms val2 = (((Object)(object)StartOfRound.Instance != (Object)null) ? StartOfRound.Instance.voiceChatModule : null);
			if ((Object)(object)val2 != (Object)null && (Object)(object)p == (Object)(object)GameNetworkManager.Instance.localPlayerController)
			{
				val = val2.FindPlayer(val2.LocalPlayerName);
			}
			if (val == null || !val.IsSpeaking)
			{
				return 0f;
			}
			return val.Amplitude;
		}

		internal static SpiritVoice? Create(PlayerControllerB player, DeadBodyInfo body)
		{
			PlayerVoiceIngameSettings currentVoiceChatIngameSettings = player.currentVoiceChatIngameSettings;
			AudioSource currentVoiceChatAudioSource = player.currentVoiceChatAudioSource;
			if ((Object)(object)currentVoiceChatIngameSettings == (Object)null || (Object)(object)currentVoiceChatAudioSource == (Object)null || player.voicePlayerState == null)
			{
				return null;
			}
			return new SpiritVoice(player, body, currentVoiceChatIngameSettings, currentVoiceChatAudioSource);
		}

		internal void Apply()
		{
			//IL_00b5: 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_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: 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_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Expected O, but got Unknown
			if (IsCurrent && player.voicePlayerState != null)
			{
				PluginConfig boundConfig = Plugin.BoundConfig;
				player.voicePlayerState.Volume = Mathf.Clamp01(boundConfig.Volume.Value);
				settings.set2D = false;
				source.spatialBlend = 1f;
				source.bypassEffects = false;
				source.bypassListenerEffects = false;
				source.rolloffMode = (AudioRolloffMode)2;
				source.maxDistance = Mathf.Max(1f, boundConfig.HearingRange.Value);
				source.SetCustomCurve((AudioSourceCurveType)0, new AnimationCurve((Keyframe[])(object)new Keyframe[3]
				{
					new Keyframe(0f, 1f),
					new Keyframe(0.15f, 1f),
					new Keyframe(1f, 0f)
				}));
				if ((Object)(object)lowPass != (Object)null)
				{
					((Behaviour)lowPass).enabled = true;
				}
				if ((Object)(object)highPass != (Object)null)
				{
					((Behaviour)highPass).enabled = true;
					highPass.cutoffFrequency = 380f;
					highPass.highpassResonanceQ = 1f;
				}
				((Behaviour)chorus).enabled = true;
				chorus.dryMix = 0.7f;
				chorus.wetMix1 = 0.35f;
				chorus.wetMix2 = 0.35f;
				chorus.wetMix3 = 0.35f;
				chorus.delay = 24f;
				chorus.rate = 0.5f;
				chorus.depth = 0.06f;
				if ((Object)(object)reverb != (Object)null)
				{
					((Behaviour)reverb).enabled = true;
					reverb.reverbPreset = (AudioReverbPreset)27;
					reverb.dryLevel = 0f;
					reverb.room = -250f;
					reverb.roomHF = -150f;
					reverb.decayTime = 3.4f;
					reverb.decayHFRatio = 1.25f;
					reverb.reflectionsLevel = -900f;
					reverb.reflectionsDelay = 0.02f;
					reverb.reverbLevel = 250f;
					reverb.reverbDelay = 0.045f;
				}
				SetPitch(1.06f);
			}
		}

		internal void Place()
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)body != (Object)null && (Object)(object)settings != (Object)null)
			{
				((Component)settings).transform.position = body.bodyParts[0].position;
			}
		}

		internal void Restore()
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)source != (Object)null)
			{
				source.rolloffMode = savedRolloff;
				source.minDistance = savedMinDistance;
				source.maxDistance = savedMaxDistance;
				source.SetCustomCurve((AudioSourceCurveType)0, savedCurve);
				source.bypassEffects = savedBypassEffects;
				source.bypassListenerEffects = savedBypassListenerEffects;
			}
			if ((Object)(object)highPass != (Object)null)
			{
				((Behaviour)highPass).enabled = savedHighPassOn;
				highPass.cutoffFrequency = savedHighPassHz;
				highPass.highpassResonanceQ = savedHighPassQ;
			}
			if ((Object)(object)lowPass != (Object)null)
			{
				((Behaviour)lowPass).enabled = savedLowPassOn;
			}
			if ((Object)(object)reverb != (Object)null)
			{
				savedReverb.ApplyTo(reverb);
				((Behaviour)reverb).enabled = savedReverbOn;
			}
			if ((Object)(object)chorus != (Object)null)
			{
				if (addedChorus)
				{
					Object.Destroy((Object)(object)chorus);
				}
				else
				{
					savedChorus.ApplyTo(chorus);
				}
			}
			SetPitch(1f);
			StartOfRound.Instance.UpdatePlayerVoiceEffects();
		}

		private void SetPitch(float pitch)
		{
			float[] array = (((Object)(object)SoundManager.Instance != (Object)null) ? SoundManager.Instance.playerVoicePitchTargets : null);
			if (array != null && player.playerClientId < (ulong)array.Length)
			{
				array[player.playerClientId] = pitch;
			}
		}
	}
	public static class PluginInfo
	{
		public const string PLUGIN_GUID = "aj.LastWords";

		public const string PLUGIN_NAME = "LastWords";

		public const string PLUGIN_VERSION = "1.0.1";
	}
}
namespace LastWords.Configuration
{
	public class PluginConfig
	{
		public ConfigEntry<string> SpeakKey;

		public ConfigEntry<float> MinSeconds;

		public ConfigEntry<float> MaxSeconds;

		public ConfigEntry<float> CooldownSeconds;

		public ConfigEntry<float> Volume;

		public ConfigEntry<float> HearingRange;

		public ConfigEntry<bool> TestKeys;

		public PluginConfig(ConfigFile cfg)
		{
			SpeakKey = cfg.Bind<string>("General", "Speak key", "<Keyboard>/r", "The key a dead player presses to go back to their body and speak (an Input System path, e.g. <Keyboard>/r or <Keyboard>/v).");
			MinSeconds = cfg.Bind<float>("Speech", "Shortest speech", 5f, "Each time, the spirit can speak for a random time between the shortest and longest (seconds). The host's setting is used.");
			MaxSeconds = cfg.Bind<float>("Speech", "Longest speech", 10f, "See \"Shortest speech\".");
			CooldownSeconds = cfg.Bind<float>("Speech", "Cooldown", 30f, "Seconds the spirit rests after speaking before it can speak again. The host's setting is used.");
			Volume = cfg.Bind<float>("Voice", "Spirit volume", 0.6f, "How loud the spirit is for the living, compared with a living player's voice (1 = as loud). Each player's own setting.");
			HearingRange = cfg.Bind<float>("Voice", "Hearing range", 15f, "How far (metres) from the body the spirit can be heard; it fades out to nothing at this distance. Each player's own setting.");
			TestKeys = cfg.Bind<bool>("Testing", "Test keys", false, "For testing only. When on, anyone can press F6 to drop dead (to have a body to speak from). Takes effect after restarting the game.");
			ClearUnusedEntries(cfg);
		}

		private void ClearUnusedEntries(ConfigFile cfg)
		{
			if (((object)cfg).GetType().GetProperty("OrphanedEntries", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(cfg, null) is Dictionary<ConfigDefinition, string> dictionary)
			{
				dictionary.Clear();
			}
			cfg.Save();
		}
	}
}